Перейти до змісту

Імпорти

Кожен файл .go оголошує пакети, які він використовує, за допомогою блоку import. Імпорти розташовані між директивою package та рештою файлу.

Базова форма

package main

import "fmt"

func main() {
    fmt.Println("hello")
}

Рядок "fmt" — це шлях імпорту. Він вказує компілятору, де знайти пакет — для пакетів стандартної бібліотеки це просто назва пакету; для сторонніх пакетів це шлях у вигляді URL, який вирішує модульна система.

import "fmt"                            // стандартна бібліотека
import "net/http"                       // підпакет стандартної бібліотеки
import "github.com/gorilla/mux"         // сторонній модуль
import "example.com/myapp/internal/db"  // внутрішній для вашого модуля

Одиночний та згрупований форми

Дві рівнозначні форми. Використовуйте згруповану, коли є більше одного імпорту — майже завжди.

import "fmt"
import "math"
import "os"

vs

import (
    "fmt"
    "math"
    "os"
)

Згрупована форма — це те, що gofmt та редактори генерують автоматично. Стандартна угода: тримати імпорти стандартної бібліотеки в одному блоці, а зовнішні — в окремому, розділеному порожнім рядком:

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/gorilla/mux"
    "go.uber.org/zap"
)

Використання імпортованих ідентифікаторів

Всередині файлу ви звертаєтесь до експортованих назв через базове ім'я пакету — зазвичай останній сегмент шляху імпорту:

import "net/http"

func main() {
    http.ListenAndServe(":8080", nil)   // базове ім'я — "http"
}

Два неочевидних правила:

  1. Базове ім'я береться з власного оголошення package пакету, а не зі шляху імпорту. Зазвичай вони збігаються, але не завжди — "yaml.v3" оголошує себе як package yaml, тож ви звертаєтесь до нього як yaml.Marshal, а не v3.Marshal.
  2. Видимі лише експортовані назви. Ідентифікатори з великої літери перетинають межі пакетів; рядкові — ні. (Дивіться правило капіталізації в 02-basic-types.md.)
import "strings"

s := strings.ToUpper("go")     // експортований — працює
s := strings.toupper("go")     // compile error — рядкова буква, не експортований

Перейменування за допомогою псевдоніму

Можна дати імпортованому пакету локальне прізвисько, написавши його перед шляхом:

import (
    f "fmt"
    rng "math/rand"
)

func main() {
    f.Println(rng.Intn(10))
}

Коли вдаватися до цього:

  • Два імпорти інакше конфліктували б (наприклад, 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" викидає всі експортовані назви з пакету в простір імен поточного файлу, щоб можна було звертатися до них без кваліфікації:

import . "math"

func main() {
    fmt.Println(Pi)            // Pi без math. — працює
}

Це настійно не рекомендується у продакшн-коді — читаючи файл, неможливо зрозуміти, звідки прийшов ідентифікатор. Єдиний законний випадок — всередині тестових файлів, яким потрібно дістатися до пакету _test, і навіть це рідкість.

З досвіду Python:from math import *. Та сама проблема, та сама порада.

Невикористані імпорти — помилка компіляції

import (
    "fmt"
    "os"            // імпортовано, але ніде не використовується
)

func main() {
    fmt.Println("hi")
}
// compile error: "os" imported and not used

Це зроблено навмисно — розробники мови хотіли, щоб списки імпортів залишалися точними. Є два виходи:

  1. Порожній імпорт: _ "os", якщо побічні ефекти справді потрібні.
  2. Тимчасове придушення під час редагування: _ = os.Getpid десь у файлі, тимчасово. Перед коммітом прибирайте це за допомогою goimports або команди впорядкування імпортів у редакторі.

З досвіду Python: Python попереджає про невикористані імпорти через лінтери (pyflakes, ruff), але ніколи не блокує компіляцію. Go підвищує це до жорсткої помилки.

Циклічні імпорти заборонені

Якщо пакет a імпортує b, то b не може імпортувати a — ані прямо, ані опосередковано.

package alpha → imports beta
package beta  → imports alpha    // compile error: import cycle

Компілятор виявляє цикли транзитивно. Якщо ви на це натрапили, рішення зазвичай таке:

  • Виокремте спільну залежність у третій пакет.
  • Перемістіть конфліктний код ближче до його викликача.
  • Використайте інтерфейс, визначений у пакеті викликача, і нехай пакет-виконавець реалізує його (ідіома 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.

Інструменти

Імпорти майже ніколи не вводять вручну. goimportsgopls, який більшість редакторів запускає при збереженні) автоматично додає відсутні та видаляє зайві — дивіться 08-additional-tools.md.

goimports -w .                 # перезаписати всі .go файли в цій директорії

Після редагування блок імпортів стає чистим і правильно згрупованим.

Коротка довідка

Форма Ефект
import "path" Прив'язати за власною назвою пакету.
import alias "path" Прив'язати під іменем alias.
import _ "path" Виконати init(), без прив'язки назви — заради побічних ефектів.
import . "path" Злити експорти пакету в простір імен цього файлу. Уникайте.

Джерела