Імпорти¶
Кожен файл .go оголошує пакети, які він використовує, за допомогою блоку import. Імпорти розташовані між директивою package та рештою файлу.
Базова форма¶
Рядок "fmt" — це шлях імпорту. Він вказує компілятору, де знайти пакет — для пакетів стандартної бібліотеки це просто назва пакету; для сторонніх пакетів це шлях у вигляді URL, який вирішує модульна система.
import "fmt" // стандартна бібліотека
import "net/http" // підпакет стандартної бібліотеки
import "github.com/gorilla/mux" // сторонній модуль
import "example.com/myapp/internal/db" // внутрішній для вашого модуля
Одиночний та згрупований форми¶
Дві рівнозначні форми. Використовуйте згруповану, коли є більше одного імпорту — майже завжди.
vs
Згрупована форма — це те, що gofmt та редактори генерують автоматично. Стандартна угода: тримати імпорти стандартної бібліотеки в одному блоці, а зовнішні — в окремому, розділеному порожнім рядком:
Використання імпортованих ідентифікаторів¶
Всередині файлу ви звертаєтесь до експортованих назв через базове ім'я пакету — зазвичай останній сегмент шляху імпорту:
Два неочевидних правила:
- Базове ім'я береться з власного оголошення
packageпакету, а не зі шляху імпорту. Зазвичай вони збігаються, але не завжди —"yaml.v3"оголошує себе якpackage yaml, тож ви звертаєтесь до нього якyaml.Marshal, а неv3.Marshal. - Видимі лише експортовані назви. Ідентифікатори з великої літери перетинають межі пакетів; рядкові — ні. (Дивіться правило капіталізації в 02-basic-types.md.)
import "strings"
s := strings.ToUpper("go") // експортований — працює
s := strings.toupper("go") // compile error — рядкова буква, не експортований
Перейменування за допомогою псевдоніму¶
Можна дати імпортованому пакету локальне прізвисько, написавши його перед шляхом:
Коли вдаватися до цього:
- Два імпорти інакше конфліктували б (наприклад,
crypto/randіmath/rand). - Базове ім'я пакету незручне або задовге для локального файлу.
Не варто задавати псевдонім лише заради скорочення — fmt.Println і так короткий.
З досвіду Python: ≈
import math as m. Та сама ідея, синтаксис перевернутий.
Порожній імпорт — _ лише для побічних ефектів¶
Порожній імпорт компілює та лінкує пакет, але не прив'язує жодної назви у вашому файлі. Посилатися на що-небудь із нього не можна. Мета — запустити функцію init() пакету заради її побічних ефектів:
import (
"database/sql"
_ "github.com/lib/pq" // реєструє драйвер "postgres"
)
func main() {
db, err := sql.Open("postgres", connStr)
// ...
}
Типові випадки: драйвери баз даних, декодери форматів зображень, хуки профілювання.
import (
_ "image/png" // реєструє декодер PNG для image.Decode
_ "image/jpeg" // реєструє декодер JPEG
_ "net/http/pprof" // реєструє обробники /debug/pprof/*
)
З досвіду Python: прямого аналога немає. Python не має форми «імпортувати заради побічних ефектів, але без назви» — кожен
importвводить ім'я.
Точковий імпорт — майже ніколи не використовуйте¶
import . "path" викидає всі експортовані назви з пакету в простір імен поточного файлу, щоб можна було звертатися до них без кваліфікації:
Це настійно не рекомендується у продакшн-коді — читаючи файл, неможливо зрозуміти, звідки прийшов ідентифікатор. Єдиний законний випадок — всередині тестових файлів, яким потрібно дістатися до пакету _test, і навіть це рідкість.
З досвіду Python: ≈
from math import *. Та сама проблема, та сама порада.
Невикористані імпорти — помилка компіляції¶
import (
"fmt"
"os" // імпортовано, але ніде не використовується
)
func main() {
fmt.Println("hi")
}
// compile error: "os" imported and not used
Це зроблено навмисно — розробники мови хотіли, щоб списки імпортів залишалися точними. Є два виходи:
- Порожній імпорт:
_ "os", якщо побічні ефекти справді потрібні. - Тимчасове придушення під час редагування:
_ = os.Getpidдесь у файлі, тимчасово. Перед коммітом прибирайте це за допомогоюgoimportsабо команди впорядкування імпортів у редакторі.
З досвіду Python: Python попереджає про невикористані імпорти через лінтери (
pyflakes,ruff), але ніколи не блокує компіляцію. Go підвищує це до жорсткої помилки.
Циклічні імпорти заборонені¶
Якщо пакет a імпортує b, то b не може імпортувати a — ані прямо, ані опосередковано.
Компілятор виявляє цикли транзитивно. Якщо ви на це натрапили, рішення зазвичай таке:
- Виокремте спільну залежність у третій пакет.
- Перемістіть конфліктний код ближче до його викликача.
- Використайте інтерфейс, визначений у пакеті викликача, і нехай пакет-виконавець реалізує його (ідіома Go «приймати інтерфейси, повертати конкретні типи»).
Стандартна бібліотека — короткий огляд¶
Найбільш вживані пакети стандартної бібліотеки:
| Імпорт | Типове використання |
|---|---|
"fmt" |
Форматований вивід: Println, Printf, Sprintf. |
"strings" |
Маніпуляції з рядками (ToUpper, Split, Replace, Contains). |
"strconv" |
Перетворення між рядками та числами. |
"errors" |
errors.New, errors.Is, errors.As. |
"os" |
Процес / файлова система, змінні середовища, os.Args. |
"io" |
Інтерфейси Reader / Writer, Copy. |
"bufio" |
Буферизований ввід/вивід, Scanner. |
"time" |
time.Now, time.Duration, таймери. |
"net/http" |
HTTP-клієнт і сервер. |
"encoding/json" |
Серіалізація / десеріалізація JSON. |
"sync" |
Mutex, WaitGroup, Once. |
"context" |
Скасування, дедлайни, значення в контексті запиту. |
"log" |
Простий логер; log/slog для структурованих логів. |
"reflect" |
Інтроспекція типів під час виконання. |
Повний список доступний на pkg.go.dev/std.
Інструменти¶
Імпорти майже ніколи не вводять вручну. goimports (і gopls, який більшість редакторів запускає при збереженні) автоматично додає відсутні та видаляє зайві — дивіться 08-additional-tools.md.
Після редагування блок імпортів стає чистим і правильно згрупованим.
Коротка довідка¶
| Форма | Ефект |
|---|---|
import "path" |
Прив'язати за власною назвою пакету. |
import alias "path" |
Прив'язати під іменем alias. |
import _ "path" |
Виконати init(), без прив'язки назви — заради побічних ефектів. |
import . "path" |
Злити експорти пакету в простір імен цього файлу. Уникайте. |