Зарегистрируйтесь, чтобы продолжить обучение

Пакеты 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 нет возможности импортировать только одну переменную или функцию из пакета.

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

GO111MODULE=off go run . # GO111MODULE — это переменная окружения, которая обозначает, включены модули или нет

Hello, Hexlet!

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

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

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

GO111MODULE=off 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, которая служит точкой входа в программу
  • Чтобы экспортировать функцию или переменную из пакета, достаточно дать ей название с заглавной буквы

Самостоятельная работа

  1. В проекте hexlet-go создайте директорию greeting
  2. В директории greeting создайте файл hello.go и добавьте туда код:
   package greeting

   var greeting string = "Golang for Brave!"

   func Hello() string {
       return greeting
   }
  1. Для локального импорта проект должен быть правильно настроен как Go модуль. Для этого инициализируйте Go модуль в вашем проекте. Вам нужно будет выполнить команду go mod init hexlet-go в корневой директории проекта.
  2. В корневой директории переименуйте файл hello.go в main.go, импортируйте в него пакет greeting и выведите на экран результат работы функции Hello():
   package main

   import (
       "fmt"
       // Здесь должен быть импорт пакета greeting
   )

   func main() {
       fmt.Println(greeting.Hello())
   }
  1. Запустите программу и убедитесь, что на экран вывелась строка Golang for Brave!
  2. Залейте все изменения на гитхаб

Дополнительные материалы

  1. Standard Go Project Layout
  2. How to write Go code

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff