Работа с данными
Отличие make и new
Make и new – это встроенные механизмы для выделения памяти. Они используются в разных ситуациях и имеют свои особенности.
- new инициализирует нулевое значение для данного типа и возвращает указатель на этот тип.
- make используется исключительно для создания и инициализации срезов, отображений и каналов, возвращает ненулевой экземпляр указанного типа.
- Основное отличие между ними состоит в том, что make возвращает инициализированный тип, готовый к использованию после создания, а new – указатель на тип с его нулевым значением.
Скрытые данные в слайсах
Слайс — это массив переменной длины, который может хранить элементы одного типа. Внутренне представляет собой ссылку на базовый массив.
При работе со слайсами часто возникает задача их «перенарезки» на более мелкие. В итоге получившийся слайс будет ссылаться на массив исходного. Об этом не стоит забывать, иначе в программе может возникнуть непредсказуемое потребление памяти.
Рассмотрим эту особенность на конкретных примерах:
Для предотвращения возникшей ошибки следует удостовериться, что копирование производится из временного слайса:
Функции
Функции с множественным возвратом
Функции в языке Go могут возвращать несколько значений. Это называется «множественным возвратом». Данная особенность языка позволяет возвращать не только результат, но и дополнительные значения, такие как ошибки или другие необходимые данные.
Пример объявления функции с множественным возвратом в Go:
В приведенном примере функция swap
принимает два аргумента типа int
и возвращает два значения того же типа, меняя местами исходные переменные.
Можно также игнорировать одно или несколько возвращаемых значений, используя пустой идентификатор (_
).
Функции с множественным возвратом особенно полезны, когда требуется возвращать несколько результатов, например, при работе с ошибками или при параллельной обработке данных.
Приведенная ниже функция openFile
возвращает два значения, одно из которых – ошибка или nil
в случае ее отсутствия.
Интерфейсы
В Go интерфейсы представляют собой набор методов, определяющих поведение объекта. Они позволяют абстрагироваться от конкретной реализации и работать с различными типами данных. То есть интерфейсы лишь определяют некоторый функционал, но сами его не реализуют.
Используем интерфейсы правильно
Ниже представлен пример неправильного подхода при работе с интерфейсами:
Верное решение с точки зрения Go — вернуть конкретный тип и позволить Worker
имитировать реализацию employer
:
Конкурентность и параллелизм
Отслеживание горутин
Горутины дешевы в запуске и эксплуатации, но у них есть конечная стоимость с точки зрения занимаемой памяти – вы не можете создать их бесконечное количество. В отличие от переменных, среда выполнения Go не может обнаружить, что горутина больше никогда не будет использоваться.
- Подробно горутины рассмотрены в статье «Горутины: что такое и как работают». Её прочтение поможет лучше разобраться в рассматриваемой теме.
go
в своей программе для запуска горутины, вы должны знать, как и когда она завершится.Если вы не знаете ответа на два приведённых вопроса, это может привести к возникновению утечек памяти.
Обратимся к примеру для иллюстрации данной ошибки:
Здесь функция leakGoroutine
запускает горутину, которая блокирует чтение из канала ch
. В результате в него ничего не отправится, и сам он никогда не закроется. Горутина будет заблокирована навсегда, вызов функции fmt.Println
никогда не произойдет.
Обнаружение утечек
Инженеры из Uber, которые принимают активное участие в развитии Go, создали детектор утечек горутин – пакет goleak, нацеленный на интеграцию с модульными тестами. Рассмотрим пример работы с этим инструментом на практике.
Пусть есть некая функция leakGoroutin
с утечкой горутины:
И тест этой функции:
При запуске тестов появляется сообщение об ошибке found enexpected goroutines
, где указывается вершина стека с проблемной горутиной, ее состояние и идентификатор.
Этот инструмент может быть полезен при создании программ, так как позволяет сократить время на нахождение и устранение утечек памяти.
Обработка ошибок и восстановление
Ошибки в Go представлены интерфейсом error, который определяет метод Error() string
. Любой тип, реализующий этот метод, может быть использован как ошибка.
- Чтобы больше узнать об ошибках в Go, рекомендуется прочитать статью «Исключения в Go – это легко?». Из нее вы узнаете о том, как эффективно решать проблемные ситуации в программах.
Обрабатываем ошибки правильно
Игнорирование ошибок может привести к неопределенному поведению и усложнить отладку кода. Рассмотрим правильный способ обработки ошибок на примере работы с файлом:
Без паники, но с восстановлением
Классический способ сообщить об ошибке – вернуть тип error
. Но что делать в тех случаях, когда её нельзя быстро восстановить? Тогда на помощь приходит встроенная функция panic
(часто её называют просто «паника»), которая завершает программу и выводит настраиваемое сообщение об ошибке.
Ниже представлен пример простой функции с паникой:
При возникновении паники функция завершается и происходит запуск оставшихся отложенных функций с помощью defer, а также раскручивание стека горутин. В реальных условиях разработки следует избегать подобных ситуаций, так как это ставит под угрозу бесперебойную работу программы. К счастью, авторы Go предусмотрели этот недостаток и создали механизм восстановления после паники – recover
. Он позволяет остановить раскручивание стека и вернуть разработчику контроль над программой.
Чтобы продемонстрировать работу данного механизма, обратимся к примеру:
В результате выполнения кода мы получим следующий вывод:
Заметьте, что функция Panic
не завершается после паники. Это происходит из-за того, что с помощью defer вызывается отложенная функция Recovery
, которая восстанавливает работу программы. Далее исполнение передается в main
, где происходит успешное завершение всего кода.
Заключение
Важно помнить, что качество и чистота кода зависят не только от языка программирования, но и от навыков разработчика. Использование рассмотренных примеров и следование общим принципам помогут улучшить качество создаваемого программного обеспечения.
Хочется верить, что статья вдохновит читателей применять описанные практики в разработке на Go и создавать программы, в которых будет нетрудно разобраться даже новичку. И помните, чистый код – это путь к успешному проекту!
Комментарии