Как писать код на Go

Введение

Этот документ демонстрирует разработку простого Go пакета внутри модуля и знакомит с инструментом go, стандартным способом получения, сборки и установки Go модулей, пакетов и команд.

Организация кода

Программы на Go организованы в пакеты. Пакет — это коллекция исходных файлов в одном каталоге, которые компилируются вместе. Функции, типы, переменные и константы, определённые в одном исходном файле, видны всем другим исходным файлам внутри того же пакета.

Репозиторий содержит один или несколько модулей. Модуль — это коллекция связанных Go пакетов, которые выпускаются вместе. Обычно Go репозиторий содержит только один модуль, расположенный в корне репозитория. Файл с именем go.mod объявляет путь модуля: префикс импортного пути для всех пакетов внутри модуля. Модуль содержит пакеты в каталоге, содержащем его файл go.mod, а также подкаталоги этого каталога, до следующего подкаталога, содержащего другой файл go.mod (если такой существует).

Обратите внимание, что вам не нужно публиковать свой код в удалённом репозитории, чтобы его можно было собрать. Модуль может быть определён локально без принадлежности к репозиторию. Однако хорошей практикой является организация кода так, как если бы вы собирались опубликовать его когда-нибудь.

Путь каждого модуля служит не только префиксом импортного пути для его пакетов, но и указывает, где команда go должна искать его для загрузки. Например, чтобы загрузить модуль golang.org/x/tools, команда go обратится к репозиторию, указанному по адресу https://golang.org/x/tools (подробнее здесь).

Импортный путь — это строка, используемая для импорта пакета. Импортный путь пакета — это путь модуля, объединённый с подкаталогом внутри модуля. Например, модуль github.com/google/go-cmp содержит пакет в каталоге cmp/. Импортный путь этого пакета — github.com/google/go-cmp/cmp. Пакеты стандартной библиотеки не имеют префикса пути модуля.

Ваша первая программа

Чтобы скомпилировать и запустить простую программу, сначала выберите путь модуля (мы будем использовать example/user/hello) и создайте файл go.mod, объявляющий его:

$ mkdir hello # Альтернативно, клонируйте его, если он уже существует в системе контроля версий.
$ cd hello
$ <b>go mod init example/user/hello</b>
go: creating new go.mod: module example/user/hello
$ cat go.mod
module example/user/hello

go 1.16
$

Первая инструкция в исходном файле Go должна быть package name. Исполняемые команды всегда должны использовать package main.

Далее создайте файл с именем hello.go внутри этой директории, содержащий следующий код Go:

package main

import "fmt"

func main() {
  fmt.Println("Hello, world.")
}

Теперь вы можете собрать и установить эту программу с помощью инструмента go:

$ <b>go install example/user/hello</b>
$

Эта команда собирает команду hello, создавая исполняемый бинарный файл. Затем она устанавливает этот бинарный файл как $HOME/go/bin/hello (или, в Windows, %USERPROFILE%\go\bin\hello.exe).

Каталог установки управляется переменными среды GOPATH и GOBIN среды выполнения. Если установлена переменная GOBIN, бинарные файлы устанавливаются в этот каталог. Если установлена переменная GOPATH, бинарные файлы устанавливаются в подкаталог bin первого каталога в списке GOPATH. В противном случае, бинарные файлы устанавливаются в подкаталог bin стандартного GOPATH ($HOME/go или %USERPROFILE%\go).

Вы можете использовать команду go env для переносимой установки значения переменной среды по умолчанию для будущих команд go:

$ go env -w GOBIN=/somewhere/else/bin
$

Чтобы удалить переменную, ранее установленную с помощью go env -w, используйте go env -u:

$ go env -u GOBIN
$

Команды, такие как go install, применяются в контексте модуля, содержащего текущую рабочую директорию. Если рабочая директория не находится внутри модуля example/user/hello, команда go install может завершиться ошибкой.

Для удобства, команды go принимают пути относительно рабочей директории и по умолчанию используют пакет в текущей рабочей директории, если другой путь не указан. Таким образом, в нашей рабочей директории следующие команды эквивалентны:

$ go install example/user/hello
$ go install .
$ go install

Далее, давайте запустим программу, чтобы убедиться, что она работает. Для удобства, мы добавим каталог установки в переменную PATH, чтобы сделать запуск бинарных файлов простым:

# Пользователям Windows следует обратиться к /wiki/SettingGOPATH
# для установки %PATH%.
$ <b>export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))</b>
$ <b>hello</b>
Hello, world.
$

Если вы используете систему контроля версий, теперь будет хорошим временем инициализировать репозиторий, добавить файлы и зафиксировать ваше первое изменение. Опять же, этот шаг является необязательным: использовать систему контроля версий для написания кода на Go не обязательно.

$ <b>git init</b>
Initialized empty Git repository in /home/user/hello/.git/
$ <b>git add go.mod hello.go</b>
$ <b>git commit -m "initial commit"</b>
[master (root-commit) 0b4507d] initial commit
1 file changed, 7 insertion(+)
create mode 100644 go.mod hello.go
$

go команда находит репозиторий, содержащий указанный путь модуля, запрашивая соответствующий HTTPS URL и читая метаданные, встроенные в HTML-ответ (см. go help importpath). Многие сервисы хостинга уже предоставляют эти метаданные для репозиториев, содержащих код на Go, поэтому самый простой способ сделать ваш модуль доступным для использования другими — это сделать так, чтобы путь модуля совпадал с URL репозитория.

Импорт пакетов из вашего модуля

Давайте создадим пакет morestrings и используем его в программе hello. Сначала создайте директорию для пакета под названием $HOME/hello/morestrings, а затем файл с именем reverse.go в этой директории со следующим содержимым:

// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings

// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
  r := []rune(s)
  for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
    r[i], r[j] = r[j], r[i]
  }
  return string(r)
}

Поскольку функция ReverseRunes начинается с заглавной буквы, она является экспортируемой, и может быть использована в других пакетах, которые импортируют пакет morestrings.

Давайте проверим, что пакет компилируется с помощью go build:

$ cd $HOME/hello/morestrings
$ <b>go build</b>
$

Эта команда не создаст выходной файл. Вместо этого она сохранит скомпилированный пакет в локальном кэше сборки.

После того как вы убедились, что пакет morestrings успешно собирается, давайте используем его в программе hello. Для этого измените ваш исходный файл $HOME/hello/hello.go, чтобы он использовал пакет morestrings:

package main

import (
  "fmt"

  <b>"example/user/hello/morestrings"</b>
)

func main() {
  fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

Установите программу hello:

$ <b>go install example/user/hello</b>

Запустив новую версию программы, вы должны увидеть новое, перевёрнутое сообщение:

$ <b>hello</b>
Hello, Go!

Импорт пакетов из удалённых модулей

Путь импорта может описывать, как получить исходный код пакета с помощью системы управления версиями, такой как Git или Mercurial. Инструмент go использует это свойство для автоматического получения пакетов из удалённых репозиториев. Например, чтобы использовать github.com/google/go-cmp/cmp в вашей программе:

package main

import (
  "fmt"

  "example/user/hello/morestrings"
  "github.com/google/go-cmp/cmp"
)

func main() {
  fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
  fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

Теперь, когда у вас появилась зависимость от внешнего модуля, необходимо загрузить этот модуль и записать его версию в файл go.mod. Команда go mod tidy добавляет недостающие требования к модулям для импортируемых пакетов и удаляет требования к модулям, которые больше не используются.

$ go mod tidy
go: finding module for package github.com/google/go-cmp/cmp
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4
$ go install example/user/hello
$ hello
Hello, Go!
string(
  -     "Hello World",
  +     "Hello Go",
)
$ cat go.mod
module example/user/hello

go 1.16

<b>require github.com/google/go-cmp v0.5.4</b>
$

Зависимости модулей автоматически скачиваются в подкаталог pkg/mod каталога, указанного переменной окружения GOPATH. Содержимое скачанного модуля для заданной версии разделяется между всеми другими модулями, которые требуют эту версию, поэтому команда go помечает эти файлы и каталоги как доступные только для чтения. Чтобы удалить все загруженные модули, можно передать флаг -modcache команде go clean:

$ go clean -modcache
$

Тестирование

Go имеет лёгкую систему тестирования, состоящую из команды go test и пакета testing.

Тест пишется путём создания файла с именем, заканчивающимся на _test.go, который содержит функции с именами TestXXX с сигнатурой func (t *testing.T). Фреймворк тестирования запускает каждую такую функцию; если функция вызывает функцию, обозначающую сбой, такую как t.Error или t.Fail, то тест считается не пройденным.

Добавьте тест в пакет morestrings, создав файл $HOME/hello/morestrings/reverse_test.go, содержащий следующий код на Go.

package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
  cases := []struct {
    in, want string
  }{
    {"Hello, world", "dlrow ,olleH"},
    {"Hello, 世界", "界世 ,olleH"},
    {"", ""},
  }
  for _, c := range cases {
    got := ReverseRunes(c.in)
    if got != c.want {
      t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
    }
  }
}

Затем запустите тест с помощью команды go test:

$ cd $HOME/hello/morestrings
$ <b>go test</b>
PASS
ok  	example/user/hello/morestrings 0.165s
$

Выполните команду go help test и ознакомьтесь с документацией пакета testing для получения дополнительной информации.

Дальнейшие шаги

Подпишитесь на рассылку golang-announce, чтобы получать уведомления о выходе новых стабильных версий Go.

См. Effective Go для советов по написанию ясного, идиоматического Go кода.

Пройдите A Tour of Go чтобы изучить язык.

Посетите страницу документации для набора подробных статей о языке Go и его библиотеках и инструментах.

Получение помощи

Для получения помощи в реальном времени, задавайте вопросы полезным gophers в сообществе gophers Slack server (забрать приглашение можно здесь).

Официальный список рассылки для обсуждения языка Go — Go Nuts.

Сообщайте об ошибках с помощью Go issue tracker.

GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang