Go: Настройка окружения

Теория: Пакеты

Go-environment

В этом уроке мы подробнее разберем работу с пакетами в Go: научимся создавать, экспортировать и импортировать их.

Создание пакета

Любой код в Go существует внутри пакета — это папка с go-файлами. Заголовки всех файлов в пакете начинаются одинаково — package имя-пакета. Даже если мы создаем программу из одного файла, она все равно должна быть внутри пакета.

Начнем работу с первым Go-пакетом. Создайте папку ./greeting/, а внутри нее — файл constants.go. Добавьте в файл такой код:

// Файл ./greeting/constants.go
package greeting // Как всегда, объявляем пакет в начале файла

var greeting string = "Hello, Hexlet!" // Дальше пишем сам код

В сообществе Go-разработчиков принято давать имена пакетам только в нижнем регистре. Не должно быть camelCase, snake_case или kebab-case. Если очень нужно использовать больше одного слова, то стоит использовать аббревиатуру. Хороший пример — пакет bufio (buffer io) из стандартной библиотеки.

Проведем небольшой эксперимент — добавим в папку еще один файл getters.go со следующим содержимым:

// Файл ./greeting/getters.go
package greeting

func Get() string {
  return greeting
}

Обратите внимание, что переменная greeting объявлена в одном файле, а используется уже в другом. В отличие от других языков, в Go компилятор и линтер не будут ругаться на такой код. Неважно, на сколько файлов разбит пакет, потому что внутри пакета общая область видимости.

Пакет main

Чтобы Go-программу можно было скомпилировать и запустить из командной строки, используют пакет main:

// Файл ./main.go
package main

import "fmt"

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

Его единственное отличие от любого другого пакета — в нем содержится функция main, которая не принимает аргументы и не возвращает значения. Команды go build и go run ищут функцию main внутри пакета main и собирают исполняемый файл, который вызывает функцию main. Эта функция — точка входа в программу.

Импорт и экспорт

В Go используется оригинальная система импортов. Воспользуемся созданными выше файлами и изучим импорт на их примере:

// Файл ./main.go
package main

import (
  "./greeting" // Относительный импорт
  "fmt"
)

func main() {
 fmt.Println(greeting.Get())
}

Структура проекта выглядит так:

├── greeting
│   ├── constants.go
│   └── getters.go
└── main.go

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

Теперь запустим программу из командной строки:

go run .

Hello, Hexlet!

Импортируются ли все переменные из пакета? Обновим функцию main так, чтобы она использовала переменную greeting:

func main() {
  fmt.Println(greeting.greeting)
}

А теперь запустим:

go run . # Ошибка! ./main.go:9:14: cannot refer to unexported name greeting.greeting

Компилятор выдает ошибку: программа не может ссылаться на не экспортируемое значение. Рассмотрим подробнее, что это значит.

Go определяет, экспортируется идентификатор или нет, по его имени:

  • Если идентификатор начинается с заглавной буквы, значение экспортируется
  • Если идентификатор начинается со строчной буквы или нижнего подчеркивания, значение не экспортируется (доступно только в своем пакете)

Алиасы

Go-разработчик может оказаться в ситуации, когда нужно импортировать два пакета с одинаковыми именами. Например, он решил выпустить вторую версию пакета greeting и создал папку v2 внутри папки greeting. Внутри этой папки всего один файл greeting.go:

// Файл ./greeting/v2/greeting.go
package greeting // Обратите внимание, что имя пакета не обязательно совпадает с именем директории, в которой находится

func Get() string {
  return "Hello from version 2!"
}

Обновим пакет main:

package main

import (
  greetingv1 "./greeting"
  greetingv2 "./greeting/v2"
  "fmt"
)

func main() {
  fmt.Println("Первое приветствие: ", greetingv1.Get(), "\n", "Второе приветствие: ", greetingv2.Get())
}

Чтобы импортировать два разных пакета с одинаковым именем, мы добавляем алиасы к импортам пакетов. Теперь можно обращаться к пакету первой версии по имени greetingv1, а ко второй — greetingv2. Если убрать алиасы, то компилятор вернет ошибку greeting redeclared as imported package name.

В Go есть способ импортировать все экспортируемые переменные из пакета, при этом не используя имя пакета. Если дать пакету алиас ., то из пакета импортируются все переменные и функции, будто бы они были объявлены внутри текущего пакета.

Выводы

Кратко перечислим основные выводы этого урока:

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

Рекомендуемые программы

Завершено

0 / 5