Пакети та видимість¶
Програму на Go організовано в пакети. Пакет — це каталог із файлами
.go, які компілюються разом і ділять один простір імен. Кожен вихідний
файл починається з оголошення package, що називає пакет, до якого він
належить.
Усе інше тримається на двох правилах:
- Один каталог = один пакет. Усі файли
.goв каталозі мають оголошувати однакову назву пакета; разом вони утворюють цей пакет. package mainособливий — це точка входу виконуваної програми, і він мусить міститиfunc main(). Кожен інший пакет — це бібліотека, яку імпортують інші.
Шляхи імпорту проти назв пакетів¶
Пакет ви імпортуєте за його шляхом імпорту (його розташуванням), а
звертаєтеся до нього за назвою пакета (ідентифікатором в оголошенні
package). Зазвичай це останній елемент шляху, але не завжди:
Назва пакета — це те, що з'являється в коді, тож її обирають за тим, як вона читається в місці виклику.
Експортоване проти неекспортованого: керування доступом — це регістр¶
Ідентифікатор, чия назва починається з великої літери, є експортованим — видимим коду в інших пакетах. З малої — неекспортований — видимий лише всередині свого пакета. Це стосується кожного імені верхнього рівня: типів, функцій, змінних, констант і полів структур.
package store
type Item struct {
Name string // експортоване поле
price int // неекспортоване поле
}
func New() *Item { return &Item{} } // експортована
func reset(i *Item) { i.price = 0 } // неекспортована
Межа приватності — це пакет, а не тип чи файл. Файли в одному пакеті вільно бачать неекспортовані імена одне одного; інший пакет бачить лише експортовані. З іншого пакета компілятор це забезпечує:
// у пакеті main, що імпортує пакет store вище
i := store.New()
fmt.Println(i.Name) // ок — Name експортоване
fmt.Println(i.price) // compile error: i.price undefined
// (cannot refer to unexported field price)
З погляду Python: немає ні суто рекомендаційної домовленості
_private, ні спотворення імен__name— видимість забезпечує компілятор, а одиниця приватності — це пакет, а не клас.
Файли в одному пакеті¶
Розбиття пакета на кілька файлів суто організаційне — файли ділять один простір імен, і порядок оголошень не має значення (функція може викликати іншу, оголошену пізніше, у будь-якому файлі пакета).
// item.go
package store
func New() *Item { return &Item{price: basePrice} }
// price.go
package store
const basePrice = 100 // видима для item.go без жодного імпорту
Функції init¶
Пакет може оголосити одну або кілька func init() — без параметрів, без
результатів. Вони запускаються автоматично під час ініціалізації
пакета, після того як налаштовано всі змінні рівня пакета, і перед стартом
main. Імпорти пакета ініціалізуються першими, тож на момент запуску
вашого init усе, від чого ви залежите, вже готове.
package config
var settings map[string]string
func init() {
settings = map[string]string{"env": "dev"}
}
Порядок — спершу змінні рівня пакета, потім init, потім main — можна
спостерігати:
var x = setup()
func setup() int { fmt.Println("var init"); return 1 }
func init() { fmt.Println("init func") }
func main() { fmt.Println("main") }
// output:
// var init
// init func
// main
Користуйтеся init помірно — для налаштування, яке справді неможливо
виразити звичайним ініціалізатором змінної. Кілька init (навіть у різних
файлах) виконуються в порядку, у якому файли подано компілятору.
Домовленості про назви¶
- Назви пакетів короткі, з малих літер, одне слово —
http,bytes,strconv. Без під_креслень чи camelCase. - Уникайте затинання. Кваліфікатор пакета вже є в місці виклику, тож
називайте
bytes.Buffer, а неbytes.BytesBuffer;store.New, а неstore.NewItem, коли пакет і так усе пояснює. - Док-коментар до пакета —
// Package store ...— розміщують над оголошеннямpackageв одному файлі, і він документує весь пакет.
Жодних циклів імпорту¶
Імпорти пакетів мають утворювати ациклічний граф: якщо a імпортує b,
то b не може імпортувати a, ні прямо, ні транзитивно. Компілятор
відхиляє цикли беззастережно. Коли два пакети ніби потребують одне одного,
це знак, що спільний тип має переїхати до третього пакета, який вони обидва
імпортують.
Швидка довідка¶
| Поняття | Правило |
|---|---|
| оголошення package | кожен файл .go починається з package X |
| один каталог | рівно один пакет |
package main + func main() |
збирає виконувану програму |
| шлях імпорту | де пакет; вживається в import |
| назва пакета | як ви звете його в коді |
Uppercase |
експортоване (видиме іншим пакетам) |
lowercase |
неекспортоване (приватне для пакета) |
func init() |
запускається під час ініціалізації пакета, перед main |
| цикл імпорту | заборонено |