Tarkibga o'tish

Go’da modullar va paketlar

Go loyihasida paket kodni bo‘limlarga ajratadi, modul esa shu paketlarni versiyalash va tarqatish chegarasini belgilaydi. Bu farqni bilish import yo‘llarini to‘g‘ri tuzish va dependencylarni boshqarish uchun kerak.

Paket nima?

Paket — odatda bitta katalogda joylashgan va bir xil package nomiga ega Go fayllari to‘plami. Paket kompilyatsiya, nomlar sohasi va kodni qayta ishlatish birligidir.

Asosiy qoidalar:

  • har bir .go fayl package <nom> qatori bilan boshlanadi;
  • bitta katalogdagi oddiy .go fayllar odatda bitta paketga tegishli bo‘ladi;
  • import qilinadigan nom paket ichida emas, paketlar orasida yagona bo‘lishi kerak;
  • package main va func main() birga bajariladigan dastur kirish nuqtasini hosil qiladi.

Test fayllari bundan alohida holat: ular shu paket nomidan yoki _test qo‘shimchali tashqi test paketidan foydalanishi mumkin.

main paketi

Quyidagi to‘liq dastur bajariladigan paket yaratadi:

main.go
package main

import "fmt"

func main() {
    fmt.Println("Go paketi bilan ishlash!")
}
go run main.go

Natija:

Go paketi bilan ishlash!

package main kompilyatorga bajariladigan dastur yaratilishini bildiradi. main() esa dastur boshlanadigan funksiya. main() boshqa paketda yozilsa, u oddiy nom bo‘lib qoladi va kirish nuqtasi hisoblanmaydi.

Masalan, fayl package yangi_nom bilan boshlansa, uni buyruq sifatida ishga tushirib bo‘lmaydi:

package command-line-arguments is not a main package

Info

Kutubxona paketida main() bo‘lishi shart emas. U boshqa paketlar import qiladigan funksiyalar, turlar va o‘zgaruvchilarni taqdim etadi.

Modul nima?

Modul — go.mod fayli bilan belgilangan paketlar to‘plami. Modul yo‘li uning ichidagi paketlar import yo‘lining boshlanishi bo‘ladi.

Yangi loyiha yaratamiz:

mkdir mening-loyiham
cd mening-loyiham
go mod init example.com/mening-loyiham

Natijada taxminan quyidagi fayl yaratiladi:

go.mod
module example.com/mening-loyiham

go 1.xx

module direktivasi modulning kanonik yo‘lini beradi. go direktivasi modul semantikasi va qo‘llanadigan til versiyasi uchun eng kichik talabni ifodalaydi; u kompyuterga Go’ni o‘rnatmaydi. Toolchain yaratgan aniq qiymat muhitga bog‘liq.

Lokal paket yaratish

Modul ichida kichik hisob paketini tuzamiz:

Loyiha strukturasi
mening-loyiham/
├── go.mod
├── main.go
└── hisob/
    └── hisob.go

hisob/hisob.go:

hisob/hisob.go
package hisob

func Qosh(a, b int) int {
    return a + b
}

func Kvadrat(n int) int {
    return n * n
}

func ayir(a, b int) int {
    return a - b
}

main.go:

main.go
package main

import (
    "fmt"

    "example.com/mening-loyiham/hisob"
)

func main() {
    fmt.Println(hisob.Kvadrat(3))
    fmt.Println(hisob.Qosh(3, 2))
}

Loyiha ildizida dasturni ishga tushiring:

go run .

Natija:

9
5

Import yo‘li example.com/mening-loyiham modul yo‘li va /hisob katalogidan tuzildi. Go importda fayl nomini emas, paket joylashgan katalog yo‘lini ishlatadi. go run . esa joriy katalogdagi main paketini build qilib ishga tushiradi.

Kvadrat uchun math.Pow kerak emas. Butun sonni o‘ziga ko‘paytirish aniqroq va float64ga ortiqcha konvertatsiyani chetlab o‘tadi. Juda katta int qiymatlarda ko‘paytirish overflow qilishi mumkin; bu misolda kichik qiymat ishlatilgan.

Eksport qilinadigan nomlar

Go’da identifikator bosh harf bilan boshlansa, u paketdan eksport qilinadi. Qosh va Kvadrat boshqa paketdan chaqiriladi. Kichik harf bilan boshlangan ayir esa faqat hisob paketi ichida ko‘rinadi.

Quyidagi chaqiruv kompilyatsiya xatosi beradi:

// Bu kod boshqa paket ichida kompilyatsiya bo‘lmaydi:
// fmt.Println(hisob.ayir(5, 2))

Xato mazmuni hisob.ayir eksport qilinmaganini bildiradi. Faqat nomni katta harf bilan boshlash yetarli emas: eksport qilingan API tushunarli nom, aniq vazifa va hujjatga ham ega bo‘lishi kerak.

Paket nomini odatda qisqa, kichik harfli va birlik shaklida tanlang: http, json, user. utils, common yoki helpers kabi umumiy nomlar paket mas’uliyatini yashirib qo‘yishi mumkin.

Import qanday topiladi?

Go import yo‘lini uch qism orqali tushunadi:

  1. joriy modul yo‘li go.moddan olinadi;
  2. modul ichidagi qolgan qism katalogni ko‘rsatadi;
  3. tashqi modul kerak bo‘lsa, uning versiyasi modul grafigidan tanlanadi.

Import yo‘li va paket nomi ko‘pincha katalog oxiri bilan bir xil, lekin bu majburiy emas. Masalan, paket boshqa nom e’lon qilsa, kodda aynan e’lon qilingan nom ishlatiladi. Tushunarlilik uchun ularni bir xil saqlash ma’qul.

Nomlar to‘qnashsa, importga lokal nom berish mumkin:

import hisoblash "example.com/mening-loyiham/hisob"

Bunda paket hisoblash.Qosh(...) shaklida chaqiriladi. Aliasni faqat nomlar to‘qnashuvi yoki tushunarlilik uchun ishlating.

Modul fayllari va dependency grafigi

go.modda quyidagi direktivalar uchrashi mumkin:

  • module — joriy modul yo‘li;
  • go — modulning Go versiyasi talabi;
  • require — dependency modul va tanlangan versiya;
  • replace — modul manbasini boshqa yo‘l yoki versiyaga vaqtincha almashtirish;
  • exclude — ma’lum versiyani tanlashdan chiqarish.

replace lokal ishlab chiqishda qulay, masalan:

replace example.com/shared => ../shared

Warning

Lokal katalogga yozilgan replace boshqa kompyuterda ishlamasligi mumkin. Uni commit qilishdan oldin jamoa va CI muhiti shu yo‘lni topa olishini tekshiring.

go.sum yuklangan modullar uchun nazorat summalarini saqlaydi. U lock-faylning aynan o‘zi emas: versiya tanlovi go.mod va modul grafigi orqali amalga oshadi, go.sum esa kontent yaxlitligini tekshirishga yordam beradi.

Foydali buyruqlar:

go mod tidy
go list -m all
go mod graph
go mod download

go mod tidy importlarga qarab go.mod va go.sumni tartiblaydi. go list -m all build ro‘yxatini, go mod graph modul bog‘lanishlarini ko‘rsatadi. go mod download dependencylarni keshga oldindan yuklaydi; odatiy build ham kerakli modullarni avtomatik yuklay oladi.

Versiya tanlashning muhim qoidalari

Go bir modul yo‘li uchun dependencylar talab qilgan versiyalar orasidan minimal talablarni qanoatlantiradigan build ro‘yxatini tuzadi. Natijada bir xil modul yo‘lining ikki versiyasi odatda bitta buildda yonma-yon tanlanmaydi.

v2 va undan katta asosiy versiyalar odatda modul hamda import yo‘liga /v2 kabi qo‘shimcha oladi:

import "example.com/lib/v2/client"

Shu sabab example.com/lib va example.com/lib/v2 Go uchun ikki xil modul yo‘lidir. Bu katta API o‘zgarishini eski foydalanuvchilarga majburan yuklamaslikka yordam beradi.

internal paketlar

Modul ichidagi ayrim paketlarni tashqi foydalanuvchilardan yashirish uchun katalogni internal deb nomlash mumkin:

mening-loyiham/
├── go.mod
├── internal/
│   └── config/
└── main.go

Go internal katalogidagi paketni faqat uning ruxsat etilgan ota daraxti ichidagi kod import qilishiga yo‘l qo‘yadi. Bu katta loyihada ommaviy API va ichki implementatsiya chegarasini kompilyator darajasida saqlaydi.

Keng tarqalgan xatolar

Nisbiy import ishlatish

Modul rejimida ./hisob kabi import yozmang. To‘liq modul yo‘lidan foydalaning:

import "example.com/mening-loyiham/hisob"

Import cycle yaratish

a paketi bni, b esa yana ani import qilsa, Go dasturni kompilyatsiya qilmaydi. Umumiy turlarni uchinchi kichik paketga ajratish yoki dependency yo‘nalishini qayta loyihalash kerak.

Faylni alohida ishga tushirish

Paket bir nechta fayldan iborat bo‘lsa, go run main.go boshqa fayllarni chetda qoldirishi mumkin. Modul ildizidagi main paketi uchun ko‘pincha go run . to‘g‘ri tanlovdir.

Har katalogga alohida modul yaratish

Har bir paketga go.mod kerak emas. Odatda bitta repozitoriydagi o‘zaro bog‘liq paketlar bitta modul ichida bo‘ladi. Alohida versiyalash va mustaqil release zarur bo‘lgandagina bir nechta modulni ko‘rib chiqing.

Interviewda nimalarga e’tibor beriladi?

  • paket kompilyatsiya va nomlar sohasi birligi, modul esa versiyalash birligi;
  • eksport katta harf bilan belgilanadi, ammo bu faqat paket chegarasiga taalluqli;
  • go.mod versiya talablarini, go.sum esa modul kontentining nazorat summalarini saqlaydi;
  • import cycle taqiqlangan;
  • v2+ modul yo‘li odatda asosiy versiyani o‘z ichiga oladi.

Xulosa

Paket kodni mantiqiy qismlarga ajratadi. Modul esa paketlarning import yo‘li, dependencylari va versiyalash chegarasini belgilaydi. Lokal paket importida modul yo‘li bilan katalog yo‘lini birlashtiring, ommaviy API uchun eksport qoidalariga amal qiling va modul fayllarini Git’da saqlang.

Keyingi darsda modulga tashqi kutubxona qo‘shish, undan keyin esa eksport qilinadigan API uchun Go dokumentatsiyasini yozishni ko‘ramiz.

Misollar

1. Eng kichik modul yaratish

Bu misolda bitta bajariladigan paket uchun yangi modul boshlanadi.

package main

import "fmt"

func main() {
    fmt.Println("Birinchi modul ishladi")
}
mkdir birinchi-modul
cd birinchi-modul
go mod init example.com/birinchi-modul
go run .

go mod init joriy katalogda go.mod yaratadi. example.com/birinchi-modul modul yo‘li, package main esa bajariladigan paket nomi. go run . joriy katalogdagi paketni ishga tushiradi.

2. Paket darajasidagi nomlardan foydalanish

Bu misolda o‘zgaruvchi va funksiya main paketining umumiy nomlar sohasida e’lon qilinadi.

package main

import "fmt"

var greeting = "Salom"

func message(name string) string {
    return greeting + ", " + name
}

func main() {
    fmt.Println(message("Go"))
}
go mod init example.com/package-scope
go run .

greeting va message() funksiyalardan tashqarida e’lon qilingani uchun shu paket ichida ko‘rinadi. Kichik harf bilan boshlangan bu nomlar boshqa paketga eksport qilinmaydi. Modul yo‘li paket ichidagi nomlar sohasini o‘zgartirmaydi.

3. Eksport qilinadigan va ichki nomlarni ajratish

Bu misolda bosh harfli hamda kichik harfli funksiyalar bir paket ichida ishlatiladi.

package main

import "fmt"

func PublicMessage() string {
    return "eksport qilinadigan nom"
}

func privateMessage() string {
    return "faqat paket ichidagi nom"
}

func main() {
    fmt.Println(PublicMessage())
    fmt.Println(privateMessage())
}
go mod init example.com/export-demo
go run .

Ikkala funksiya ham ayni main paketida bo‘lgani uchun chaqiriladi. Boshqa paket faqat PublicMessage()ni import orqali ishlata oladi. privateMessage() kichik harf bilan boshlanganligi sabab paketning ichki nomi bo‘lib qoladi.

4. init() va main() bajarilish tartibi

Bu misolda paket tayyorlanayotganda init(), undan keyin main() ishga tushadi.

package main

import "fmt"

var status = "boshlanmadi"

func init() {
    status = "tayyor"
    fmt.Println("init:", status)
}

func main() {
    fmt.Println("main:", status)
}
go mod init example.com/init-demo
go run .

status avval matn bilan yaratiladi. Keyin init() uni tayyor qiymatiga almashtiradi. main() boshlanganda shu yangilangan qiymat mavjud bo‘ladi. init()ni koddan bevosita chaqirish kerak emas.

5. Importga lokal nom berish

Bu misolda fmt paketi lokal format nomi bilan import qilinadi.

package main

import format "fmt"

func main() {
    format.Println("Paket alias orqali chaqirildi")
}
go mod init example.com/import-alias
go run .

Import yo‘li o‘zgarmaydi: paket hali ham standart kutubxonadagi fmt. Faqat shu faylda unga format nomi bilan murojaat qilinadi. Alias odatda nomlar to‘qnashganda yoki mavjud nom ma’noni yetarli ifodalamaganda kerak bo‘ladi.

6. Bir nechta paketni guruhlab import qilish

Bu misolda xato yaratish va uni chiqarish uchun ikkita standart paket import qilinadi.

package main

import (
    "errors"
    "fmt"
)

func main() {
    err := errors.New("namunaviy xato")
    fmt.Println(err)
}
go mod init example.com/grouped-imports
go run .

Qavsli import guruhi har bir yo‘lni alohida satrda saqlaydi. Kod paketlarni errors va fmt nomlari bilan ishlatadi. gofmt import guruhining joylashuvini bir xil ko‘rinishga keltiradi.

7. Joriy modul yo‘lini ko‘rish

Bu misolda modul yaratilib, uning kanonik yo‘li Go buyrug‘i orqali tekshiriladi.

package main

import "fmt"

func main() {
    fmt.Println("Modul yo‘li go.mod faylida saqlanadi")
}
go mod init example.com/module-path
go list -m
go run .

go list -m joriy asosiy modul yo‘lini chiqaradi. Natija example.com/module-path bo‘ladi. Bu yo‘l keyin modul ichidagi lokal paketlar import yo‘lining boshlanishi sifatida ishlatiladi.

8. Joriy paket haqidagi ma’lumotni olish

Bu misolda go list joriy katalogdagi paket nomi va import yo‘lini ko‘rsatadi.

package main

import "fmt"

func main() {
    fmt.Println("Joriy paket: main")
}
go mod init example.com/package-info
go list -f "{{.Name}} {{.ImportPath}}" .
go run .

Shablondagi .Name paket e’lonidan main qiymatini oladi. .ImportPath esa modul yo‘li va katalog joylashuviga asoslanadi. Ildiz katalogda natija main example.com/package-info ko‘rinishida bo‘ladi.

9. Modul faylini tartibga keltirish

Bu misolda go mod tidy kod importlari bilan modul faylini moslashtiradi.

package main

import "fmt"

func main() {
    values := []int{2, 4, 6}
    fmt.Println("Qiymatlar:", values)
}
go mod init example.com/tidy-demo
go mod tidy
go run .

Bu dastur faqat standart fmt paketidan foydalanadi, shu sabab go mod tidy tashqi require yozuvi qo‘shmaydi. Buyruqning vazifasi importlar va modul fayllarini mos holatda saqlashdir. Bo‘sh dependency ro‘yxati ham to‘g‘ri natija hisoblanadi.

10. Moduldan bajariladigan fayl qurish

Bu misolda go build joriy main paketidan bajariladigan fayl yaratadi.

package main

import "fmt"

func main() {
    fmt.Println("Build qilingan dastur")
}
go mod init example.com/build-demo
go build -o build-demo .

go build paketni tekshiradi va kompilyatsiya qiladi. -o build-demo natija fayli nomini aniq belgilaydi. . joriy katalogdagi main paketini anglatadi; paket kutubxona bo‘lsa, bajariladigan fayl yaratilmaydi.