На чем написан golang
Язык Go для начинающих
Цель этой статьи — рассказать о языке программирования Go (Golang) тем разработчикам, которые смотрят в сторону этого языка, но еще не решились взяться за его изучение. Рассказ будет вестись на примере реального приложения, которое представляет из себя RESTful API веб-сервис.
Передо мной стояла задача разработать бэкэнд к мобильному сервису. Суть сервиса довольно проста. Мобильное приложение, которое показывает посты пользователей, находящихся рядом с текущим местоположением. На посты пользователи могут оставлять свои комментарии, которые тоже, в свою очередь, можно комментировать. Получается своеобразный гео-форум.
Давно хотел попробовать применить язык Go для сколь нибудь серьезных проектов. Выбор был очевиден, благо что этот язык как нельзя лучше подходит для подобных задач.
Основные преимущества языка Go:
Все эти, и многие другие особенности позволяют выделить язык среди остальных. Это достойный кандидат на изучение, к тому же, освоить язык довольно просто.
Итак, вернемся к нашей задаче. Хоть язык и не накладывает ограничений на структуру проекта, данное приложение я решил организовать по модели MVC. Правда View реализовывается на стороне клиента. В моем случае это был AngularJS, в перспективе — нативное мобильное приложение. Здесь я расскажу лишь об API на стороне сервиса.
Структура проекта получилась следующая:
Программа в Go разделяется на пакеты (package), что указывается в начале каждого файла. Имя пакета должно соответствовать директории в которой находятся файлы, входящие в пакет. Так же, должен быть главный пакет main с функцией main(). Он у меня находится в корневом файле приложения loctalk.go. Таким образом, у меня получилось 5 пакетов: conf, controllers, models, utils, main.
Буду приводить неполное содержание файлов, а только минимально необходимое для понимания.
Пакет conf содержит константы и настройки сайта.
Думаю, комментировать тут нечего. Функция init() вызывается в каждом пакете до вызова main(). Их может быть несколько в разных файлах.
В самом верху определяется имя пакета. Далее идет список импортируемых пакетов. Мы будем использовать пакет Martini. Он добавляет легкую прослойку для быстрого и удобного создания веб-приложений. Обратите внимание как импортируется этот пакет. Нужно указать путь к репозиторию откуда он был взят. А чтобы его получить, достаточно в консоли набрать команду go get github.com/go-martini/martini
Далее мы создаем экземпляр Martini, настраиваем и запускаем его. Обратите внимание на знак « := ». Это сокращенный синтаксис, он означает: создать переменную соответствующего типа и инициализировать ее. Например, написав a := «hello», мы создадим переменную a типа string и присвоим ей строку «hello».
Переменная m в нашем случае имеет тип *ClassicMartini, именно это возвращает martini.Classic(). * означает указатель, т. е. передается не само значение, а лишь указатель на него. В метод m.Use() мы передаем функцию-обработчик. Этот Middleware позволяет Martini делать определенные действия над каждым запросом. В данном случае, мы определяем Content-Type для каждого запроса. Метод m.Map() же позволяет привязать нашу структуру и использовать ее затем в контроллерах при необходимости (механизм dependency injection). В данном случае, я создал обертку для кодирования структуры данных в формат json.
Тут же мы создаем внутреннюю функцию Auth, которая проверяет авторизацию пользователя. Ее можно вставить в наши роуты и она будет вызываться до вызова контроллера. Эти вещи возможны благодаря Martini. С использованием стандартной библиотеки код получился бы немного другой.
Взглянем на файл errors.go пакета conf.
Язык поддерживает возврат нескольких значений. Вместо механизма try-catch, очень часто используется прием, когда вторым аргументом возвращается ошибка. И при ее наличии, она обрабатывается. Есть встроенный тип error, который представляет из себя интерфейс:
Таким образом, чтобы реализовать этот интерфейс, достаточно иметь метод Error() string. Я создал свой тип для ошибок ApiError, который более специфичен для моих задач, однако совместим со встроенным типом error.
Обратите внимание на — type ApiError struct. Это определение структуры, модели данных, которую вы будете использовать постоянно в своей работе. Она состоит из полей определенных типов (надеюсь, вы успели заметить, что тип данных пишется после имени переменной). Кстати, полями могут быть другие структуры, наследуя все методы и поля. В одинарных кавычках « указаны теги. Их указывать не обязательно. В данном случае они используются пакетом encoding/json для указания имени в выводе json (знак минус «-» вообще исключает поле из вывода).
Обратите внимание, что поля структуры написаны с заглавной буквы. Это означает, что они имеют область видимости за пределами пакета. Если написать их с прописной буквы, они экспортироваться не будут, а будут доступны только в пределах пакета. Это же относится и к функциям и методам. Вот такой простой механизм инкапсуляции.
Далее мы определяем предустановленные ошибки и заполняем их поля. Поля вида http.StatusBadRequest — это значения типа int в пакете http для стандартных кодов ответа, своего рода алиасы. Мы используем сокращенный синтаксис объявления структуры &ApiError<> с инициализацией. По другому можно было бы написать так:
Символ & означает получить указатель на данную структуру. Оператор new() так же возвращает указатель, а не значение. По-началу возникает небольшая путаница с указателями, но, со временем, вы привыкните.
Перейдем к нашим моделям. Приведу урезанную версию модели постов:
Здесь мы используем замечательный драйвер для MongoDb — mgo, чтобы сохранять данные. Для удобства, я создал небольшую обертку над api mgo — utils.NewDbSession. Логика работы с данными: сначала мы создаем объект во внутренней структуре языка, а затем, с помощью метода этой структуры, сохраняем его в базу данных.
Обратите внимание, что в этих методах мы везде используем наш тип ошибки conf.ApiError. Стандартные ошибки мы конвертируем в наши с помощью conf.NewApiError(err). Так же, важен оператор defer. Он исполняется в самом конце выполнения метода. В данном случае, закрывает соединение с БД.
Что ж, осталось взглянуть на контроллер, который обрабатывает запросы и выводит json в ответ.
Здесь мы получаем из URL id запрашиваемого поста, создаем новый экземпляр нашей структуры и вызываем на ней метод LoadById(id) для загрузки данных из БД и заполнения данной структуры. Которую мы и выводим в HTTP ответ, предварительно преобразовав в json нашим методом mu.Marshal(post).
Обратите внимание на сигнатуру функции:
Входные параметры нам предоставляет Martini с помощью механизма внедрения зависимостей (dependency injection). И мы возвращаем два параметра (int, []byte) — число (статус ответа) и массив байт.
Итак, мы разобрали основные компоненты и подходы, используя которые, вы сможете сделать эффективный RESTful API интерфейс в короткие сроки. Надеюсь, статья была полезна и вдохновит некоторых из вас заняться изучением замечательного языка Go. Уверен, за ним будущее.
Для изучения могу порекомендовать хорошую книгу на русском «Программирование на языке Go» Марка Саммерфильда. И, конечно, больше практиковаться.
Зачем вам учить Go
Источник картинки
Go — относительно молодой, но популярный язык программирования. По данным опроса Stack Overflow, именно Golang получил третье место в рейтинге языков программирования, которые хотели бы освоить разработчики. В этой статье мы попробуем разобраться в причинах популярности Go, а также посмотрим, где этот язык используется и почему его вообще стоит изучать.
Немного истории
Язык программирования Go был создан компанией Google. Собственно, его полное название Golang — производное от «Google language». Несмотря на то, что в анонсе язык был назван молодым, в этом году ему исполняется уже десять лет.
Цель, которая стояла перед создателями Go — разработать простой и эффективный язык программирования, который мог бы использоваться для создания качественного программного обеспечения. Роб Пайк, один из создателей Go, заявил, что Go разрабатывался для программистов компании, которые относительно недавно закончили учиться и знают Java, C, C++ или Python. Go для них — язык, в котором можно быстро разобраться и к которому быстро привыкаешь.
Изначально он был инструментом внутри Google, но с течением времени вышел из недр корпорации и стал достоянием общественности.
Преимущества языка
У Golang есть большое количество плюсов, как известных, так и не очень.
Простота. Собственно, это была основная цель создания языка, и ее удалось достичь. У Go достаточно простой синтаксис (с определенными допущениями), поэтому приложения можно разрабатывать быстрее, чем на некоторых других языках. И здесь есть два интересных момента.
Во-первых, Golang достаточно быстро может изучить полный новичок в программировании — тот, кто не знает вообще ни одного языка и только собирается стать разработчиком. О Go можно сказать, что он почти такой же несложный (относительно, конечно), как PHP или даже Pascal, но такой же мощный, как С++.
Во-вторых, Go может освоить уже «сформировавшийся программист», тот, кто уже знает один или несколько языков. Чаще всего разработчики изучают Go после того, как освоили Python или PHP. Далее некоторые программисты с успехом используют пару Python/Go или PHP/Go.
Большое количество библиотек. Если вам не хватает какой-либо возможности в Go, можно воспользоваться одной из множества библиотек и выполнить требуемую задачу. У Go есть еще одно преимущество — можно без проблем взаимодействовать с библиотеками языка Си. Есть даже мнение, что Go-библиотеки — это обертки для C-библиотек.
Чистота кода. Компилятор Go позволяет держать код «чистым». К примеру, неиспользуемые переменные считаются ошибкой компиляции. В Go решается большая часть проблем форматирования. Это делается, к примеру, при помощи программы gofmt при сохранении или компиляции. Форматирование правится автоматически. Подробнее обо всем этом можно узнать в туториале Effective.
Статическая типизация. Еще одно преимущество Go, благодаря которому снижается вероятность допущения ошибки разработчиком. Да, первые пару дней программист, привыкший к динамической типизации, раздражается при необходимости объявлять тип для каждой переменной и функции, равно, как и для всего остального. Но потом становится понятно, что здесь сплошные плюсы.
GoDoc. Утилита, которая очень сильно упрощает документирование кода. Большим плюсом GoDoc является то, что здесь не используются дополнительные языки вроде JavaDoc, PHPDoc или JSDoc. Утилита использует максимальное количество информации, которую она извлекает из документируемого кода.
Обслуживание кода. Его легко обслуживать именно благодаря простому и лаконичному синтаксису. Все это — наследие Google. Поскольку у корпорации огромное количество кода для различных программных продуктов, а также десятки тысяч разработчиков, которые все это разбирают, то появляется проблема обслуживания. Код должен быть понятен всем, кто над ним работает, хорошо документированным и лаконичным. Все это возможно с Go.
При этом в Golang нет классов (есть структуры, struct), нет поддержки наследования, что значительно упрощает изменение кода. Плюс нет исключений, аннотаций и т.п.
Что можно написать на Go
Практически все, за исключением некоторых моментов (например, разработки, связанные с машинным обучением — здесь больше подходит все же Python с низкоуровневыми оптимизациями на C/C++ и CUDA).
Все остальное можно писать, особенно это актуально в отношении web-сервисов. Кроме того, на Go стоит разрабатывать приложения как для конечного пользователя, так и для разработки демонов, UI, он подходит для кроссплатформенных приложений и сервисов.
Востребованность Golang
С течением времени язык становится все более востребованным. Кроме тех компаний, что присутствуют на картинке выше, с Golang работают Mail.ru Group, Avito, Ozon, Lamoda, BBC, Canonical и другие.
«Мы решили масштабировать бизнес, нам важно построить принципиально новую технологическую платформу, которая обеспечит быстрое развитие продукта. Делаем ставку на Go из-за его скорости и надёжности, а главное — аудитории программистов, которая его использует», — заявили представители Ozon в 2018 году, после того как компания приняла решение перейти на Golang.
Ну а что насчет доходов заработная плата Go-разработчика в прошлом году составила в среднем 60-140 тыс. рублей по данным «Моего Круга». По сравнению с 2017 годом этот показатель увеличился на 8,3%. В 2019 году, скорее всего, рост продолжится, поскольку разработчики Golang нужны очень многим компаниям.
Что дальше?
Останавливаться в развитии Golang точно не будет. Потребность в хороших специалистах, которые знают этот язык, будет только возрастать, так что работу специалисту (начинающему или профи) найти будет несложно. В принципе, это утверждение актуально и сейчас, поскольку на рынке IT наблюдается постоянный дефицит разработчиков.
Go хорош как для начинающих программистов, так и для профи, кто уже знает один или несколько языков программирования. Выучить его или переучиться может практически любой программист.
Статья готовилась совместно с преподавателем курса Golang в GeekBrains Сергеем Кручининым, за что ему огромное спасибо!
Национальная библиотека им. Н. Э. Баумана
Bauman National Library
Персональные инструменты
Go (язык программирования)
Содержание
История
В сентябре 21 в 2007 году, Роберт Гризмер, Роб Пайк и Кен Томпсон начали делать первые наброски основных целей на белой доске. В течении нескольких дней, цели были собраны в план того, что нужно сделать «нечто» и в некую идею того, как это должно выглядеть в конце концов. Проектирование продолжалось в свободное время от основной работы. В январе 2008 года, Кен начал работу над компилятором с целью исследовать некоторые идеи. На выходе компилятор выдавал код на C. В середине года, язык перерастает в полноценный проект. В мае 2008 года, Ян Тейлор, независимо от других, начал разработку внешнего интерфейса GCC для Go используя черновой вариант спецификации. Рус Кокс присоединился к проекту в конце 2008 года и помог воплотить прототипы языка и его библиотек в реальность. 10 ноября 2009 года, Go становится проектом с открытым исходным кодом.
Go является попыткой совместить простоту программирования в интерпретируемом, динамически типизированном языке с производительностью и надёжностью статически типизированного, компилируемого языка. Go также стремится быть современным, с возможностью сетевого и многоядерного вычисления. В конце концов работа с Go обязана быть быстрой: компиляция большой программы на одном компьютере должна занимать несколько секунд. Для достижения этих целей необходимо решение ряда языковых проблем: выразительный, но лёгкий тип системы; многопоточность (конкуренция) и сборщик мусора, жёсткая спецификация языка и т. д. Всё это не может быть лёгко решено только лишь с помощью библиотек и инструментов; необходим новый язык.
Go в большой степени отоносится к семейству C подобных языков (из-за синтаксиса). На язык так же оказало влияние семейство Pascal/Modula/Oberon (объявления и пакеты), плюс некоторые идеи из языков вдохновлённых концепцией Тони Хоара CSP таких как Newsqueak и Limbo (многопоточность-конкуренция). Несмотря на всё это наследие, Go является новым языком. В каждом аспекте язык был разработан думая о том, что делает программист и как сделать программирование, или хотя бы какой либо его аспект, более эффективным, то есть более интересным. [3]
Разработчики в Google столкнулись со следующими проблемами: [4]
Разработчики приняли решение попытаться решить эти проблемы с помощью нового языка программирования с поддержкой сборщика мусора, мультипоточности (конкуренции) и быстрой компиляции. В соответствии пунктам выше:
Принципы и дизайн языка
Отметим некоторые моменты, которые Go не поддерживает: [6]
Именование
Для именования переменных Go разработчики используют «camelCaseStyle», но есть один очень важный момент
Это означает, что можно получить доступ за пределами пакета, если пакет импортирован. К примеру
Тот же принцип применяется для полей структур
Синтаксис объявления
Новички в Go (особенно C разработчики) часто сталкиваются с проблемой понимания синтаксиса объявления в Go. Сравним с синтаксисом в С. Каждый разработчик на C знает о «спиральном правиле»
Данные объявления понимаются легко. Рассмотрим более сложный пример
Начиная с этого момента объявление читается не очевидно до тех пор, пока не освоен принцип объявления в C. Go пытается разрешить эту проблему используя стиль объявления слева направо
Возвращение нескольких значений
Одной из особенностей в Go является возможность возвращать несколько переменных. Такая возможность позволяет решить несколько проблем, с которыми приходилось сталкиваться в C: к примеру возвращение ошибки при чтении/записи в файл.
В Go, сигнатура метода записи в файл имеет следующий вид
Массивы и Слайсы
Свойство значения может быть полезным, но затратным. Если необходимо поведение массива как в C, то можно передавать указатель на массив.
Метод Read принимает слайс, вместо указателя на массив и количества байт необходимых считать; длина внутри слайса устанавливает верхний лимит на то, сколько байт данных необходимо считать. Сигнатура метода Read из пакета os:
Метод возвращает количество считанных байт и значение error. Чтобы считать первые 32 байта из buf мы используем слайс.
Такая практика очень часто используется и в других функциях. Для большой информации о слайсах можно прочитать статью «Arrays, slices (and strings): The mechanics of ‘append» и «Go Slices: usage and internals»
Обработка ошибок
Для большей информации о том, почему в Go нет обработки исключительных ситуаций «Why does Go not have exceptions?» и «Errors are Values».
Интерфейсы
Если что то может делать это, значит это может быть использовано здесь. [8]
Рассмотрим интерфейс Interface в пакете sort [9]
Если мы хотим использовать этот интерфейс нам достаточно сделать import "sort" и реализовать методы для нашего типа а именно: Len, Less и Swap.
После реализации методов мы можем использовать функцию sort.Sort() в которую в качестве аргумента необходимо передать значение, тип которого имеет методы Len, Less и Swap.
Многопоточность (конкуренция)
В этой секции мы лишь рассмотрим как легко запустить код в конкурентном режиме. Для этого достаточно лишь сказать go
Мультипоточность и конкуренция является большой темой обсуждения в программировании. Для лучшего понимания о том, что из себя представляет конкуренция и то, как она используется в Go рекомендуется прочитать серию статей/презентаций.
Go и Go!
Фрэнсис МакКейб, разработчик языка программирования Go!, опубликовал сообщение, где он требует Google сменить название языка. [10] Но после долгих дебатов в октябре 2010 года тема была закрыта со статусом неудачно.
Если вы подумываете начать писать на Go, то вот что вам следует знать
Ваш любимый питомец пишет на Go и получает больше вас, а вы ещё нет? Не теряйте времени… Такая мысль может родиться у читателя от обилия статей по Go. Некоторым даже компании предлагают переучиться на этот язык. И, если вы хоть раз задумывались освоить язык, то я хочу вас предостеречь. Вернее показать странные вещи, попробовать объяснить зачем они и потом вы уже сами сделаете вывод нужен ли вам Go.
Для кого эта статья
Статья в первую очередь предназначена тем людям, для которых важна выразительность языка. И одновременно для тех, кто хочет пощупать Go.
Я сам Си++/Python разработчик и могу сказать, что это сочетание является один из оптимальнейших для освоения Go. И вот почему:
Что по-поводу пары Java/C#? Go ей ни разу не конкурент, по крайней мере пока он молод (речь про версию Go 1.11).
Чего не будет в статье
А что будет? Только конкретные случаи дискомфорта, которые доставляет язык в работе.
Начало работы
Хорошим вводным по языку мануалом является короткая онлайн книга Введение в программирование на Go. Читая которую вы довольно быстро наткнётесь на странные особенности. Приведём для начала первую партию из них:
Странности компилятора
Поддерживаются только египетские скобки, то есть следующий код не компилируется:
Авторы считают, что стиль программирования должен быть единообразным и компактным. Чтож хозяин — барин.
Здесь упор идёт на то, что почти всегда это ошибка, связанная или с опечаткой, или спешкой, или кривым рефакторингом. Как бы в конечном коде да, такого быть не должно. Но мы редко пишем сразу конечный код и периодически пробуем запускать промежуточные версии, в которых может быть некоторый задел на будущее. Поэтому данное поведение компилятора напрягает.
Правда со временем возникает множество ситуаций, когда это уберегло от ошибки. Но это всё-равно напрягает.
Неиспользуемые параметры приходится заглушать и это смотрится странно, хотя в питоне так тоже можно:
Но это всё цветочки и даже просто вкусовщина разработчиков. Теперь перейдём к более тяжеловесным вещам.
«Безопасный» язык
И тут надо не забыть сказать об очень важной вещи. Дело в том, что язык сделан именно таким, чтобы неопытным разработчики не имели возможности создавать плохие программы.
Вот цитата одного из создателей языка:
«Ключевой момент здесь, что наши программисты (прим.пер.: гуглеры) не исследователи. Они, как правило, весьма молоды, идут к нам после учебы, возможно изучали Java, или C/C++, или Python. Они не в состоянии понять выдающийся язык, но в то же время мы хотим, чтобы они создавали хорошее ПО. Именно поэтому язык должен быть прост для понимания и изучения.»
Так значит вы говорите безопасный язык?
и после запуска программы получаем:
В этом невинном примере мы «забыли» выделить себе память и получили ошибку времени выполнения. Так а какой безопасности может идти речь, если вы меня не спасли от неверной ручной работы по выделению ресурсов?
Хабраюзер tyderh замечает, что:
Безопасность заключается в том, что при выполнении отлавливается ошибка, а не происходит неопределённое поведение, способное произвольным образом изменить ход выполнения программы. Таким образом, подобные ошибки программистов не способны привести к появлению уязвимостей.
Вызовет ошибку компиляции, что как бы нормально. Но поскольку в Go пока (пока!) нет шаблонов, то очень часто они эмулируются через интерфейсы, что может рано или поздно вылиться в такой код:
Этот код уже компилируется и работает, но не так как ожидает программист. Все три сравнения выдадут false, ибо сначала сравнивается тип интерфейсов, а он разный. И если в данном случае ошибка явно бросается в глаза, в реальности она может быть сильно размыта.
powerman поделился ещё один примером ложных ожиданий:
Интерфейс с nil не равен просто nil, будьте осторожны. В FAQ языка этот момент есть.
Ну и завершая про безопасность. Разыменование в языке убрано, а вот спецэффекты в зависимости от вида доступа от доступа (по указателю или по копии) остались. Поэтому следующий код:
Выведет pen. А следующий:
Выведет «-deleted-«, но пожалуйста, не ругайте сильно программистов, когда они на эти грабли наступят, от этого в «безопасном» языке их не спасли.
а потом вы решили добавить второй параметр возврата, IDE подсказал вам, что теперь надо его ловить, и вам пришлось попутно заменить оператор и вдруг вы видите грабли перед лицом.
А это значит, что теперь нам надо у PVS просить статический анализатор для языка Go.
Краткий вывод: безопасность присутствует, но она не абсолютна от всего.
«Единообразный» язык
Выше в разделе странности компилятора было указано, что при неверном форматировании кода, компилятор упадёт. Я предположил, что это было сделано для единообразия кода. Посмотрим насколько код единообразный.
Вот например, два способа выделить память:
Ну да, ну да, это просто фишка функции new, которую мало кто использует. Ладно будем считать это не критичным.
Вот например, два способа создать переменную:
Ладно, ладно, var используется реже и в основном для резервирования под именем определённого типа или для глобальных переменных неймспейса.
Ладно, с натяжкой будем считать Go единообразным языком.
«Колбасный» код
А вот ещё частая проблема, конструкция вида:
Это типичный кусок кода на Go, назовём его условно колбасой. Среднестатистический код на Go состоит на половину из таких колбас. При этом первая колбаса сделана так result, err := function(), а все последующие так result, err = function(). И в этом не было бы проблемы, если бы код писался только один раз. Но код — штука живая и постоянно приходиться менять местами колбасы или утаскивать часть колбас в другое место и это вынуждает постоянно менять оператор := на = и наоборот, что напрягает.
«Компактный» язык
Когда читаешь книгу по Go, не перестаёшь удивляться компактности, кажется что все конструкции продуманы так, чтобы код занимал как можно меньше места как по высоте, так и по ширине. Эта иллюзия быстро рушится на второй день программирования.
И в первую очередь из-за «колбас», о которых я упоминал чуть выше. Сейчас ноябрь 2018 и все Go программисты ожидают версию 2.0, потому что в нём будет новая обработка ошибок, которая наконец покончит с колбасами в таком количестве. Рекомендую статью по ссылке выше, в ней суть проблемы «колбасного» кода разъяснена наглядно.
Но новая обработка ошибок не устранит все проблемы компактности. По прежнему будет не хватать конструкций in и not in. На текущий момент проверка нахождения в map значения выглядит так:
И единственное на что можно надеяться — на то, что после компиляции это будет скукожено до просто проверки значения, без инициализации попутных переменных.
Молодой язык и бедный синтаксис
К Go существует очень много написанного кода. И есть просто потрясающие вещи. Но не редко вы выбираете между очень плохой библиотекой и просто приемлемой. Например SQL JOIN в одном из лучших ORM в GO (gorm) выглядит так:
А в другом ORM вот так:
Что ставит пока под сомнение вообще необходимость использовать ORM ибо нормальной поддержки защиты от переименования полей не везде просто нет. И ввиду компилируемой природы языка может и не появиться.
А вот один из лучших образцов компактного роутинга в вебе:
Не то чтобы здесь было что-то плохое, но в динамических языках код обычно выглядит более выразительным.
Спорные недостатки
Публичные функции
Угадайте, как сделать функцию публичной для использования в других пакетах? Здесь есть два варианта: либо вы знали или никогда бы не угадали. Ответ: зарезервированного слова нет, нужно просто назвать функцию с большой буквы. В это вляпываешься ровно один раз и потом привыкаешь. Но как питонист помню про правило «явное лучше неявного» и предпочёл бы отдельное зарезервированное слово (хотя если вспомнить про двойное подчёркивание в питоне, то чья бы корова мычала).
Многоэтажность
Если вам нужен словарь объектов, то вы напишите что-то такое:
Пугающая конструкция, не правда ли? Глазу хочется каких-нибудь скобочек, чтобы не спотыкаться. К счастью они возможны:
Но это всё, что позволит вам форматтер go fmt, который почти наверняка будет использоваться в вашем проекте для переформатирования кода при сохранении. Все остальные вспомогательные пробелы будут выпилены.
Атомарные структуры
Их нет. Для синхронизации надо явно использовать мьютексы и каналы. Но «безопасный язык» не будем вам пытаться мешать писать одновременно из разных потоков в стандартные структуры и получать падение программы.
helgihabr любезно напомнил, что в 1.9 появился sync.Map.
Тестирование
Во всех не очень безопасных языках безопасность хорошо реализуется через тестирование с хорошим покрытием. В Go с этим почти всё в порядке, кроме необходимости писать колбасы в тестах:
Понимая ущербность данного подхода, мы сразу нашли в сети библиотеку, реализующую assert и доработали её до вменяемого состояния. Можно брать и использовать: https://github.com/vizor-games/golang-unittest.
Теперь тесты выглядят так:
Две конвертации типов
В языке сущность интерфейса имеет особый статус. Они в том числе часто используются, чтобы заткнуть «бедность» синтаксиса языка. Выше уже был пример с реализацией шаблонов через интерфейсы и неявным вредным спецэффектом, порождённым этим случаем. Вот ещё один пример из этой же серии.
Для преобразования типов можно использовать обычную конструкцию в Си-стиле:
Но не пытайтесь применить её к интерфейсам, ибо для них синтаксис другой:
И это довольно долго будет вас путать. Я для себя нашёл следующее правило для запоминания.
Преобразование слева называется conversion, его корректность проверяется при компиляции и в теории для констант может производится самим компилятором. Такое преобразование аналогично static_cast из Си++.
Преобразование справа называется type assertion и выполняется при выполнении программы. Аналог dynamic_cast в Си++.
Исправленные недостатки
Пакетный менеджер
vgo одобрен, поддерживается JetBrains GoLand 2018.2, для остальных IDE как временное решение подойдёт команда:
Да, это выглядит как небольшой костыль сбоку, но это отлично работает и просто реализует ваши ожидания по версионированию. Возможно в go2 этот подход будет единственным и нативным.
В версии 1.11 эта штука уже встроена в сам язык. Так что верной дорогой идут товарищи.
Достоинства
мы сразу сообщаем рантайму, что, в независимости от того каким и где будет выход из функции, в конце надо выполнить определённый код. Это сразу частично решает проблему с отсутствием деструкторов и почти полностью решает проблему отсутствующих контекстов (например питоновский with).
Почему так получилось
Go выглядит как надмножество Си. Об этом говорит очень многое: и похожесть синтаксиса и понимание того, как это может быть легко преобразовано в Си код. Конечно же горутины, сборка мусора и интерфейсы (а вместе с ним RTTI) нетипичны для Си, но весь остальной код легко конвертируется практически регулярками.
И вот эта природа, на мой взгляд, и диктует почти все приведённые выше странности.