09 / 73

Oldindan e'lon qilingan tiplar

Har bir til sonlar, matn va rostlik borasida bir nechta qaror qabul qiladi, va aynan shu qarorlar tildagi qolgan hamma narsaning qanday his qilinishini belgilaydi. Goning bu boradagi qarorlari g'ayrioddiy darajada sodda. Sonli tiplar orasida avtomatik kengaytirish yo'q, mantiqiy bo'lmagan qiymatlar uchun yashirin rostlik yo'q, va baytlar bilan matn orasidagi farqni indamay yashiradigan yagona universal "belgilar satri" abstraksiyasi ham yo'q.

Go nega shu tanlovlarni qilganini tushunganingizdan keyin, tip tizimi cheklovchi bo'lib tuyulishdan to'xtab, oldindan aytsa bo'ladigan bo'lib qoladi — aslida gap ham shunda.

Oldindan e'lon qilingan tiplar — butun sonlar, suzuvchi nuqtali sonlar, kompleks sonlar, mantiqiy tip, satr tipi va ikkita maxsus muqobil nom (alias) byte hamda rune — importsiz hamma joyda mavjud. Ular Godagi boshqa har bir tip, struct'dan tortib slice'gacha va channel'gacha bo'lgan barcha vositalarning asosiy poydevoridir.

Butun sonlar

Go sizga to'qqizta butun son tipini taqdim etadi va ularning shuncha ko'pligi bezak emas, balki tizimli arxitektura talabidir. Ikkilik fayl formatlari, tarmoq protokollari va apparat registrlari aniq bit kengligini talab qiladi. Shuning uchun Go umumiy "son" tipidan sun'iy ravishda uint16 yasash o'rniga, ularni to'g'ridan-to'g'ri til darajasida ochib beradi:

TipKenglikOraliq
int88 bit-128 dan 127 gacha
int1616 bit-32 768 dan 32 767 gacha
int32 (rune)32 bit-2 147 483 648 dan 2 147 483 647 gacha
int6464 bit-9 223 372 036 854 775 808 dan 9 223 372 036 854 775 807 gacha
intplatformaga bog'liqba'zi build'larda 32 bit, deyarli barcha zamonaviylarida 64 bit
uint8 (byte)8 bit0 dan 255 gacha
uint1616 bit0 dan 65 535 gacha
uint3232 bit0 dan 4 294 967 295 gacha
uint6464 bit0 dan 18 446 744 073 709 551 615 gacha
uintplatformaga bog'liqint bilan bir xil kenglikda, ishorasiz
uintptrplatformaga bog'liqistalgan pointer'ning bit ko'rinishini sig'dira oladigan darajada katta

int va uint tiplari int64/uint64 uchun shunchaki alias (muqobil nom) emas. Ularning aniq o'lchami apparat arxitekturasiga bog'liq: bugungi kunda Go ishlatiladigan yetakchi platformalarda (amd64, arm64) ular 64 bitga teng, biroq til spetsifikatsiyasi faqat "kamida 32 bit" bo'lishini kafolatlaydi.

64 bitli int ni nazarda tutib yozilgan kod — masalan, faqat 64 bitga sig'adigan qiymatni oddiy int ga saqlash — agar o'sha kod 32 bitli tizimlar yoki o'rnatilgan (embedded) qurilmalarda ishlashi kerak bo'lib qolsa, kutilmagan xatoliklarga olib kelishi mumkin.

Deyarli barcha Go qo'llanmalarida qabul qilingan amaliy qoida: shunchaki oddiy hisob-kitob yoki indeks uchun standart holatda int ni tanlang (sikl hisoblagichlari, slice uzunliklari, arifmetika natijalari). Aniq o'lchamli tiplarga (int32, uint64 va h.k.) esa faqat tashqi ma'lumotlar formati aniq talab qilgandagina murojaat qiling (masalan, ikkilik protokollardan 32 bitli maydonni o'qish yoki ma'lumotlar bazasi ustunining aniq kengligiga moslash).

Qiymat "hech qachon manfiy bo'lmasligi kerak" degan vaj bilan uint ni tanlash ko'pincha xavfsizlik chorasi emas, aksincha xatodir: ishorasiz butun sonlarda ayirish amali xatolik bermaydi, balki toshib ketadi (underflow bo'lib aylanib qoladi). Natijada manfiy son hosil bo'lishi o'rniga, u sezilmasdan ulkan musbat songa aylanib ketadi. uintptr esa yanada torroq: u deyarli faqat unsafe pointer arifmetikasi uchun mavjud va o'zining hech qanday xavfsizlik kafolatiga ega emas; siz uni past darajadagi runtime yoki cgo kodidan tashqarida kamdan-kam ko'rasiz.

example.go
var age int = 30
var count uint = 100
var small int8 = 127
var large int64 = 9_223_372_036_854_775_807

Literaldagi pastki chiziq belgisi (_) — bu faqat odam o'qishi uchun qulaylik yaratadigan haqiqiy Go imkoniyati (Go 1.13+). Kompilyator uni butunlay e'tiborsiz qoldiradi va uni katta sonlarni ko'rgazmali qilish uchun ishlatish juda tavsiya etiladi.

Go 1.21 versiyasida yana ikkita ichki funksiya qo'shildi — min va max. Ular math paketisiz yoki qo'shimcha solishtirishlarsiz har qanday tartiblangan tip bilan to'g'ridan-to'g'ri ishlaydi:

example.go
smallest := min(4, 9, 2)   // 2
biggest := max(4, 9, 2)    // 9

Suzuvchi nuqtali sonlar

Go IEEE 754 ikkilik suzuvchi nuqta standartini ikki kenglikda amalga oshiradi — float32 va float64 — hamda odatda kengrog'idan foydalanishni tavsiya qiladi. float64 taxminan 15-17 ta o'nlik raqam aniqligini saqlaydi (float32 ning 6-9 tasiga nisbatan). Zamonaviy protsessorlarda torroq tipdan foydalanish deyarli hech qanday tezlik ustunligini bermaydi — xotirani tejash juda muhim bo'lgan ulkan massivlar (masalan, grafika yoki audio buferlari) bilan ishlamayotgan bo'lsangiz, doimo float64 dan foydalaning.

example.go
var pi float64 = 3.14159
var temperature float32 = 98.6
result := 10.5 / 3.0  // float64 sifatida avtomatik aniqlanadi

Suzuvchi nuqtali sonlar Goda ham boshqa barcha tillardagi kabi xususiyatlarga ega: masalan, ikkilik fraktsiyalar tufayli 0.1 + 0.2 aynan 0.3 ga teng bo'lmaydi va bunday sonlarni == orqali to'g'ridan-to'g'ri solishtirish deyarli har doim noto'g'ri hisoblanadi — buning o'rniga ma'lum bir ruxsat etilgan xatolik chegarasi (epsilon) bilan tekshirish yoki math.Abs(a-b) < epsilon dan foydalanish lozim. Ushbu tipda uchta maxsus qiymat ham mavjud: musbat va manfiy cheksizlik (math.Inf(1), math.Inf(-1)) hamda "son emas" (math.NaN()) — masalan, 0.0/0.0 kabi amallar natijasi.

NaN har qanday qiymatga, hatto o'ziga ham teng emas degan o'ziga xos xususiyatga ega. Shuning uchun x != x tekshiruvi uning NaN ekanligini bildiradi; standart kutubxonadagi to'g'ri va idiomatik usuli esa — math.IsNaN(x) funksiyasidir.

Kompleks sonlar

Kompleks sonlar Go tip tizimida birinchi darajali til elementi (first-class citizen) sifatida kiritilgan — complex64 (ikkita float32 dan iborat) va complex128 (ikkita float64 dan iborat). Bu ularni faqat tashqi kutubxona sifatida taqdim etuvchi ko'pgina tillarga nisbatan noyob xususiyatdir.

example.go
c1 := complex(1, 2)      // complex128: 1+2i
c2 := 3 + 4i              // tipsiz kompleks konstanta

realPart := real(c1)     // 1
imagPart := imag(c1)     // 2

Ilmiy hisoblash, signallarni qayta ishlash va vaqti-vaqti bilan grafika yadrolaridan tashqarida ko'pchilik Go dasturchilari ishlab chiqarish kodida hech qachon complex128 yozmaydi, ammo uning mavjudligini bilib qo'yish arziydi — shunda birovning sonli paketida i ni literal qo'shimchasi sifatida ko'rganingizda hayron qolmaysiz.

Mantiqiy tip

bool aynan ikkita qiymatni saqlaydi — true va false — va Go buni shunday saqlashga qat'iy: butun son yoki pointer'dan mantiqiy tipga yashirin o'tkazish yo'q, shuning uchun if x x ning allaqachon bool bo'lishini talab qiladi — C yoki Pythondagi kabi "agar x noldan farqli bo'lsa" ma'nosida if x deb yoza olmaysiz.

Bu tilning qolgan qismi bo'ylab o'tadigan xuddi shu falsafaning kichik bo'lagi: shart aynan nimani tekshirayotganini aniq aytishi kerak.

example.go
var isReady bool = true
var isActive bool // nol qiymat: false

ready := isReady && !isActive
loud  := isReady || isActive

Satrlar

Go satri — o'zgarmas, faqat o'qish uchun mo'ljallangan baytlar slice'i, boshqa hech narsa emas. Til va standart kutubxonada o'sha baytlarga UTF-8 da kodlangan matn sifatida qarash bo'yicha kuchli kelishuv bor, ammo tipning o'zi bunday va'da bermaydi; siz satrni ixtiyoriy ikkilik ma'lumotdan yasashingiz mumkin va u uni bemalol saqlaydi.

"Tip aslida nima" (baytlar) bilan "kelishuv odatda nima bo'lishini aytadi" (UTF-8 matn) orasidagi shu farq — Go satrlari haqida o'zlashtirish kerak bo'lgan eng muhim narsa, chunki satrga oid deyarli har bir xato buni unutishdan kelib chiqadi.

example.go
s1 := "Hello"           // talqin qilinadigan: \n, \t, \uXXXX qochishlarini qo'llab-quvvatlaydi
s2 := `Hello`           // xom: qochish yo'q, bir necha qatorga cho'zilishi mumkin
s3 := `line one
line two`

len(s) ko'rinadigan belgilar sonini emas, baytlar sonini qaytaradi, va matningiz sof ASCII bo'lishdan to'xtagan zahoti bu ahamiyat kasb etadi:

example.go
len("hello")  // 5
len("世界")    // 6 — bu ikki belgining har biri UTF-8 da 3 bayt egallaydi

Satrni indekslash (s[0]) sizga belgini emas, bitta baytni beradi, va bu faqat satr ASCII ekanini allaqachon bilsangizgina ma'noli. Satrlar o'zgarmas — s[0] = 'h' kompilyatsiya xatosi — shuning uchun satrni "o'zgartirish" har doim yangisini qurish demakdir, odatda bir martalik birlashtirishdan murakkabroq har qanday narsa uchun strings.Builder orqali:

example.go
var b strings.Builder
b.WriteString("Hello, ")
b.WriteString("world")
result := b.String()

strings paketi — kundalik matn bilan ishlashning ko'p qismi yashaydigan joy: Contains, HasPrefix/HasSuffix, Split/Join, TrimSpace, ToUpper/ToLower, Replace/ReplaceAll, bo'sh joy bilan ajratilgan bo'laklarga bo'lish uchun Fields, hamda satrni ajratuvchining birinchi uchrashi atrofida Split ning xotira sarfisiz bo'lish uchun Cut (Go 1.18+). strconv paketi satr bilan son orasidagi chegara bilan ishlaydi — strconv.Itoa, strconv.Atoi, strconv.ParseFloat, strconv.FormatInt — va sof sonli formatlash uchun odatda fmt.Sprintf("%d", n) dan afzal ko'riladi, chunki u fmt keltirib chiqaradigan refleksiya yukidan qochadi.

Rune'lar va baytlar

Go "bayt" va "belgi" uchun yangi sun'iy tiplar yaratmaydi — u butun sonlardan qayta foydalanadi va ularga ma'noli nom beradi: byte — uint8 uchun muqobil nom (alias), rune esa int32 uchun muqobil nomdir. Rune bitta Unicode kod nuqtasini (code point) ifodalaydi, bu esa deyarli barcha amaliy holatlarda "bitta belgi" uchun to'g'ri birlik hisoblanadi (birlashtiruvchi belgilar va murakkab grafema klasterlaridan tashqari — bular bilan unicode va golang.org/x/text paketlari ishlaydi).

Bu farqning juda aniq amaliy oqibati bor: satr bo'ylab qanday yurishingiz uning ma'nosini butunlay o'zgartiradi.

example.go
s := "Hello, 世界"

// Baytma-bayt: faqat sof ASCII belgilar uchungina to'g'ri ishlaydi
for i := 0; i < len(s); i++ {
    fmt.Printf("%d ", s[i])
}

// Runama-rune: haqiqiy "belgilar" bo'ylab yurishning to'g'ri usuli
for i, r := range s {
    fmt.Printf("%d:%c ", i, r)
}

Satr ustidagi range sikli Go tilida maxsus holat sifatida ishlaydi: u iteratsiya davomida UTF-8 baytlarini avtomatik dekodlaydi va sizga (bayt_indeksi, rune) juftliklarini qaytaradi.

E'tibor bering: range beradigan indeks — bu belgilar soni emas, balki satr boshidan hisoblangan bayt siljishidir (byte offset). Aynan shu sababli, agar satrda ko'p baytli belgilar bo'lsa, indekslar 0, 1, 2... ketma-ketligida emas, masalan, 0, 5, 8 kabi sakrab ketadi.

Alohida belgilar bilan ishlash kerak bo'lganda — satrni teskari aylantirish, haqiqiy ko'rinadigan belgilarni sanash yoki ismning birinchi harfini katta qilishda — satrni avval []rune ga o'giring:

example.go
runelar := []rune("Hello, 世界")
fmt.Println(len(runelar)) // 9 — bayt soni emas, haqiqiy belgilar soni

[]byte ga o'tkazish esa kiritish-chiqarish (I/O) jarayonlari uchun juda muhimdir. Standart kutubxonadagi deyarli barcha past darajadagi o'qish/yozish interfeyslari — io.Reader, io.Writer, bufio, os.File — ma'lumotlarni []byte ko'rinishida uzatadi, chunki operatsion tizim aynan baytlar oqimi bilan ishlaydi. Satrni []byte ga yoki aksincha o'tkazish odatda xotirada to'liq yangi nusxa (copy) yaratadi. Amaliy qoida: foydalanuvchiga ko'rsatiladigan, loglarga yoziladigan yoki solishtiriladigan matnlarni string sifatida saqlang, []byte dan esa faqat fayl yoki tarmoq kiritish-chiqarish chegarasida yoxud baytlarni joyida o'zgartirish kerak bo'lganda foydalaning.

Tip o'tkazishlar (Type Conversions)

Goda yashirin (implicit) tiplarni o'zgartirish umuman mavjud emas — hatto C oilasidagi tillar avtomatik bajaradigan int32 dan int64 ga o'tish kabi "xavfsiz" o'tkazishlar ham qo'lda yozilishi shart. Har bir tip o'tkazish ochiq va funksiya chaqiruvi kabi ko'rinadi:

example.go
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

// var f2 float64 = i // kompilyatsiya xatosi: i (int) ni float64 ga to'g'ridan-to'g'ri o'zlashtirib bo'lmaydi

Bu ataylab qo'yilgan qat'iy cheklovdir. Toraytiruvchi o'tkazishlar — int64 dan int32 ga yoki manfiy int dan uint ga o'tish — qiymatning yuqori bitlarini kesib tashlashi yoki uni to'satdan manfiydan musbatga aylantirib yuborishi mumkin. Go bunday noxushliklar sezilmasdan sodir bo'lishining oldini oladi va dasturchidan int8(i) kabi ochiq sintaksis orqali ma'lumot yo'qotilishidan xabardor ekanligini tasdiqlashni talab qiladi:

example.go
var i int = 300
var i8 int8 = int8(i) // aylanadi: 300 mod 256, ishorali sifatida talqin qilinadi → 44

Butun sondan satrga o'tishda ham ehtiyot bo'lish kerak: string(65) sizga "65" emas, balki ASCII bo'yicha "A" belgisini beradi, chunki son satrga o'girilganda u bitta rune kodi sifatida tushuniladi. Sonni matnga aylantirish uchun strconv paketidan foydalaniladi:

example.go
s := strconv.Itoa(42)                        // "42"
n, err := strconv.Atoi("42")                 // 42, nil
f, err := strconv.ParseFloat("3.14", 64)     // 3.14, nil

Tipni avtomatik aniqlash (Type Inference) va tipsiz konstantalar

:= qisqa e'lon operatori va var x = value usuli kompilyatorga o'ng tomondagi qiymatdan kelib chiqib o'zgaruvchi tipini avtomatik aniqlash (type inference) imkonini beradi:

example.go
x := 42        // int
y := 3.14      // float64
name := "Ada"  // string

Buning tagida muhim bir tamoyil yotadi: 42 kabi yozilgan literal o'zgaruvchiga bog'lanmaguncha aniq int emas. U tipsiz konstanta (untyped constant) bo'lib, kompilyator unga kontekst aniq tip belgilamaguncha cheksiz aniqlikka ega deb qaraydi. Aynan shuning uchun ham bitta literal hech qanday qo'shimcha o'tkazishlarsiz int ga ham, float64 ga ham yoki byte ga ham tenglashtirilishi mumkin:

example.go
const big = 1 << 62
var asInt int64 = big
var asFloat float64 = big

Biroq, konstanta aniq tipli o'zgaruvchiga o'zlashtirilgach, u odatiy tipli qiymatga aylanadi va barcha qat'iy tekshiruv qoidalariga bo'ysunadi.

Oldindan e'lon qilingan primitiv tiplar Go tilining eng mustahkam poydevoridir. Keyingi bobda biz ushbu tiplarning nol qiymatlari (zero values) bilan tanishamiz: Go initsializatsiya qilinmagan o'zgaruvchilarni tasodifiy xotira chiqindilari bilan qoldirmaydi, balki har bir tip uchun aniq va xavfsiz boshlang'ich qiymatni kafolatlaydi.