Про язык Go и книгу «Programming in Go»

Талисман языка Go

В последнее время все больше стало появляться статей про язык программирования Go, который разрабатывается в стенах Google. Чтобы посмотреть, что это за зверь, прочитал книгу Марка Саммерфильда (Mark Summerfield) «Programming in Go. Creation Application for the 21th Century» из серии Develorep’s Library. Когда я писал этот пост, узнал, что эта книга в 2013 году была издана и на русском языке, но я читал ее английскую версию.

Прежде чем писать про книгу, расскажу про особенности языка Go. Я не буду давать оценки тому или иному решению, который предлагает язык, поскольку пока не напишешь на каком-нибудь языке серьезную программу, глупо его критиковать — каждый язык программирования требует настройки на «свою волну». Я постараюсь все особенности языка описать на пальцах, используя минимум кода.

Но начнем все-таки с Hello World без каких-либо комментариев, просто, чтобы увидеть, как выглядит программа на языке Go (пример взят из книжки).

// hello.go
package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    who := "World!"
    if len(os.Args) > 1 { /* os.Args[0] is "hello" or "hello.exe" */
        who = strings.Join(os.Args[1:], " ")
    }
    fmt.Println("Hello", who)
}

Go представляет собой компилируемый язык со статической типизацией, который применяет подход утиной типизации: если что-то выглядит как утка, плавает как утка и крякает как утка, то это утка. Для запуска программ, написанных на языке Go не требуется установки никаких виртуальных машин или дополнительных библиотек — все нужное хранит в себе запускаемый файл. Разумеется, он при этом несколько распухает (приведенная выше программа Hello World под Linux будет скомпилирована в запускаемый файл размером 2.3 МБ), но для больших программ это будет скорее всего не существенно. Но зато в среду выполнения Go входит сборщик мусора.

Во время чтения книги у меня возникало ощущение, что Go — это осовремененный язык C (не C++). Т.е. в Go нет классов, только типы и структуры, которые могут хранить члены, а методы привешиваются к ним отдельно. По сути мы говорим: «у нас есть такая структура с данными, а вот функция, которую можно вызывать с этим типом». Вызов функции все равно выглядит как «foo.FooMethod (param1, param2)», но функция FooMethod описывается вне структуры. Вот как это выглядит:

package main

import (
    "fmt"
)

type Foo struct {
    Bar int
}

func (foo Foo) FooMethod () {
    fmt.Println(foo.Bar)
}

func main() {
    spam := Foo{10}
    spam.FooMethod()
}

Объектно-ориентированное программирование в Go несколько необычное для тех, кто писал на C++ / Java / C# и т.п. языках. Оно, конечно, не настолько непривычное как в JavaScript, то все равно заставляет думать немного по-другому.

Утиная типизация проявляется в том, что мы создаем интерфейсы (аналогичные интерфейсам в Java или C#), но мы не должны явно объявлять, что для какая-то структура его реализует, мы просто пишем для структуры функции, которые удовлетворяют этот интерфейс. И, если нужные функции написаны, то компилятор понимает, что эту структуру можно использовать там, где ожидается данный интерфейс.

В языке нет шаблонов, или generics, или чего-то подобного. Вот это мне кажется все-таки недостатком. Если нужно создать обобщенную коллекцию, то придется использовать пустой интерфейс interface{} (более безопасный аналог void* в C), а потом приводить его к нужному типу.

В языке нет перегрузки функций и операторов. Нет отлова и обработки исключений в том виде, как это принято в C++ / Java / C# / Python. Поскольку в языке Go функция может возвращать несколько значений, то принято ошибку возвращать в качестве последнего возвращаемого значения (или nil, если ошибки нет). Это вместо исключений. Для ошибок предусмотрен специальный тип.

Правда, в языке предусмотрен отлов совсем критических ошибок (в терминах языка — паника, panic), но это нужно в первую очередь для того, чтобы программа могла бы сохранить данные и логи перед тем, как отправиться в мир иной.

Непривычно сделана работа со строками. Строки являются неизменяемыми, как в Python, и хранятся в кодировке UTF-8. Непривычность заключается в том, что получение элемента по индексу str[n] возвращает не символ, а байт в многобайтовой кодировке. Чтобы обращаться к элементам строки как к символам, получать длину строки в символах, а не в байтах, нужно использовать функции из стандартной библиотеки, что получается несколько громоздко. Правда, такой подход позволяет легко преобразовывать строки в (из) массив байтов byte[] для записи его в файл или чтения из него.

Еще одной важной особенностью языка является то, что в язык встроены инструменты для многопоточного программирования. Вместо привычных инструментов вроде мьютексов, критических секций и т.п. (они тоже есть, но в стандартной библиотеке) предлагается использовать каналы и горутины (goroutines). Это еще одна вещь, заставляющая думать о программах по-другому. Вместо работы с общей памятью (и блокировками) предлагается делать общие, безопасные с точки зрения многопоточности, каналы, через которые можно обмениваться данными, а для обработки больших данных использовать те самые горутины — это что-то вроде легковесных потоков, которые можно делать в большом количестве не сильно заботясь (в пределах разумного) о том, сколько у нас в реальности ядер — созданием физических потоков будет заниматься среда выполнения.

К языку прилагается большая стандартная библиотека, благодаря которой «из коробки» можно создавать архивы, читать и писать файлы XML и JSON. Можно даже буквально несколькими строчками создать простой веб-сервер, который будет способен обрабатывать различные адреса вида http://127.0.0.1/page, http://127.0.0.1/otherpage и т.п. Есть встроенный инструмент для юнит-тестов, измерения скорости выполнения участков кода и генерации документации по коду, функции для криптографии и подсчета контрольных сумм, для работы с изображениями в форматах png, jpeg и gif. Да, кстати, в Go есть встроенные комплексные числа и функции для работы с ними.

Что касается юнит-тестов, но я был удивлен, узнав, что в стандартной библиотеке нет аналогов функций assertEqual, assertLess и т.п. В тестах нужно самостоятельно проверять условия и вызывать функцию Fail() или ее аналог в случае ошибки. В самом языке, кстати, нет и оператора assert, разработчики это объясняют тем, что программисты слишком часто надеются на этот оператор и ленятся проверять входные данные, даже в том случае, если ошибку можно исправить, в результате чего программы падают.

Из более мелких особенностей языка можно отметить возможность отложенного запуска функций или других действий, т.е. если мы помечаем какую-то строку оператором defer, то эта строка будет выполнена не там, где она написана, а перед выходом из функции независимо от того, дойдет выполнение функции до конца или случится паника. Это аналог блока finally в других языках при обработке исключений. Такие отложенные вызовы очень удобно использовать для закрытия файлов или выполнения других подобных действий.

Видимость членов структур вне пакета, в котором они расположены, зависят от того, с большой буквы начинаются их имена (аналог public из других языков) или с маленькой (аналог protected). Например, в следующей структуре член Bar будет виден вне пакета, а foo — нет.

type Spam struct {
    Bar int
    foo string
}

Кстати, любителей писать фигурные скобки на отдельных строках (к коим отношусь и я), расстрою — в Go так писать не получится, и придется писать так, как показано в предыдущих примерах. Это связано с хитрой расстановкой точек с запятой компилятором (обратите внимание, что они нигде явно не были проставлены), но сейчас в эти подробности вдаваться не будем.

В Go есть такие понятия как ссылки и указатели, но в отличие от C, запрещена математика с указателями, а благодаря сборщику мусора, все ссылки находятся под наблюдением, поэтому, например, можно из функции возвращать указатель на переменную, созданную в самой функции (не надо думать о том, что переменная будет создана в стеке и при выходе из функции указатель будет указывать непонятно на что). В Go есть анонимные функции, которые могут образовывать замыкания (closure), обычные функции замыкания не образуют.

Авторы языка в качестве преимуществ Go называют не только скорость выполнения, но и скорость компиляции. Например, программу на Go можно запускать сразу на выполнение, минуя явную компиляцию, с помощью команды go run. Поэтому этот язык можно рассматривать как замену каким-нибудь скриптовым языкам вроде Python и Perl.

Здесь я перечислил далеко не все особенности языка, а только те, что мне показались наиболее важными.


programming_in_go

А теперь давайте все-таки пару слов скажу про книгу. Оригинал книги был написан в 2012 году, поэтому, судя по всему, там идет речь о Go 1.0, хотя в тексте везде упоминается Go 1.x. Книга мне очень понравилась, написана она довольно доходчиво, каждая, даже небольшая, программа подробно разбирается. Понравилась методика автора — он начинает использовать какую-то особенность языка, коротко говоря, что это такое, а подробное строгое описание этой возможности дает уже потом, через несколько глав, когда читатель уже понимает, как эту особенность использовать. Так, например, операторы if, for и т.п. описываются где-то в середине книги, когда, понятное дело, они уже активно использовались до этого. Но при этом все написано так, что все понятно. В течение главы всегда используются очень короткие примеры, поясняющие описываемую сущность, а в конце каждой главы разбирается несколько более длинных завершенных примеров, приближенных к практическому применению.

Не знаю как перевод, а в оригинале книга читается очень легко и интересно, даже с учетом того, что я далеко не спец по английскому языку.

Хочу теперь попробовать этот язык на практике, есть даже идеи, что написать, но пока нужно подобрать кроссплатформенную библиотеку для создания интерфейсов. Покопавшись в интернете, у меня сложилось впечатление, что в этой части пока царит анархия — есть множество заброшенных проектов, и несколько проектов, поддерживаемым одним человеком. Так что, если кто знает в какой библиотеке хорошо встраивается WebKit (или IE под Windows), то скажите мне. Ну и узнать о личном опыте использования Go тоже интересно.

На данный момент у меня сложилось впечатление, что Go хорош для написания серверных приложений, работающих под высокой нагрузкой (кстати, интересно, как у него обстоят дела с распараллеливанием в кластере, может Go заменить MPI?), там, где важна скорость.

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.

Пожалуйста, оцените запись

УжасноПлохоТак себеХорошоОтлично (Количество голосов: 5, средняя оценка: 5,00)
Загрузка...

комментария 4

  1. Serghei:

    Заказал вот себе )))

  2. klay:

    Купил себе «Programming in Go. Creation Application for the 21th Century» (Mark Summerfield) на русском 🙂

  3. Низом:

    Я новичок в этом направление. Я не знаю куда написать этот Ваш код то есть я скачал язык программирования Go из официального сайта но у меня откроется браузер (http://localhost:6060/) но больше нечего не понятно даже в интернете нету как открыть эту программу и где написать код абсолютно неизвестно….

  4. Jenyay:

    Для начала почитайте какие-нибудь статьи про начало работы с Go. Там сначала надо настроить некоторые переменные окружения, а потом работаете как с любым другим консольным компилятором из командной строки. Из коробки никакого GUI нет, но про Go знают многие текстовые редакторы и среды разработки.

Leave a comment

Subscribe without commenting