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

Пакеты 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
    }
  3. Для локального импорта проект должен быть правильно настроен как Go модуль. Для этого инициализируйте Go модуль в вашем проекте. Вам нужно будет выполнить команду go mod init hexlet-go в корневой директории проекта.

  4. В корневой директории переименуйте файл hello.go в main.go, импортируйте в него пакет greeting и выведите на экран результат работы функции Hello():

    package main
    
    import (
        "fmt"
        // Здесь должен быть импорт пакета greeting
    )
    
    func main() {
        fmt.Println(greeting.Hello())
    }
  5. Запустите программу и убедитесь, что на экран вывелась строка Golang for Brave!

  6. Залейте все изменения на гитхаб


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

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

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

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

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

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

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

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

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

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

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

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»