Ko'pchilik tillar konstantalarga kichik qulaylik sifatida qaraydi: sehrli songa nom berib, u kod bazasi bo'ylab sochilib ketmasligini ta'minlash usuli. Go ham shunday qiladi, ammo bundan qiziqroq narsani ham bajaradi: u konstantalarga o'zgaruvchilarda bo'lmagan tiplashtirish shaklini beradi, va bu kodingiz ishga tushishidan ancha oldin sonli tiplar atrofidagi juda ko'p noqulaylikni indamay hal qiladi.
Tipsiz konstantalarni tushunib olish — Goni o'rganishning boshida qilinadigan, keyin doimo foyda keltiradigan kichik sarmoyalardan biri: u nega
3.14 * someFloat32shunchaki ishlashini tushuntiradi, holbuki tiplari mos kelmaydigan ikkita oddiy o'zgaruvchi bilan yozilgan xuddi shu ifoda kompilyatsiya bo'lishdan bosh tortadi.
const kompilyatsiya paytida qat'iy belgilanadigan va hech qachon qayta o'zlashtirib bo'lmaydigan qiymatni e'lon qiladi:
const Pi = 3.14159
const Greeting = "Hello, World!"
const MaxUsers = 100
O'zaro bog'liq konstantalar bitta blokka guruhlansa, yaxshiroq o'qiladi:
const (
Red = "red"
Green = "green"
Blue = "blue"
)
Konstantalar kompilyatsiya paytida hisoblanishi shart bo'lgani uchun ular bir-biriga murojaat qila oladi, ammo o'zgaruvchiga yoki natijasi konstanta bo'lmagan funksiya chaqiruviga hech qachon murojaat qila olmaydi:
const (
Width = 100
Height = 200
Area = Width * Height // 20000 — to'g'ri, ikkala operand ham konstanta
)
var x = 10
const y = x // kompilyatsiya xatosi: x — o'zgaruvchi, konstanta emas
const z = len("hello") // to'g'ri: satr literalining len qiymati kompilyatsiya paytidagi konstanta
Oxirgi qatorda to'xtalib o'tish foydali: literalning len qiymati — kompilyator to'liq hisoblab chiqa oladigan kichik ichki funksiyalar to'plamidan biri, aynan shuning uchun u konstanta ifodasida qonuniy, holbuki ixtiyoriy slice'ning len qiymati bunday bo'lmaydi.
Mana shu yerda Go deyarli barcha boshqa statik tiplashtirilgan tillardan ajralib turadi. Tipi ochiq ko'rsatilmasdan e'lon qilingan konstanta "dinamik tiplashtirilgan" degan ma'noda tipsiz emas — u torroq va foydaliroq ma'noda tipsiz: agar tip tanlashga majbur bo'lsa, u oladigan standart tipi bor, ammo shu paytgacha u moslashuvchan bo'lib qoladi va qiymat sig'gunicha kontekst talab qilgan istalgan tipni qabul qiladi.
const x = 42 // tipsiz butun son konstantasi
var i int = 42 // bu yerda x int bo'ladi
var f float64 = x // bu yerda float64
var c complex128 = x // bu yerda complex128
var b byte = x // bu yerda esa byte
Bu o'zlashtirishlarning har biri hech qanday ochiq o'tkazishsiz kompilyatsiya bo'ladi, chunki x o'zlashtirish majbur qilmaguncha hech qanday tipga bog'lanmagan edi. Buni ikkita o'zgaruvchi bilan solishtiring:
var i int = 42
var f float64 = float64(i) // ochiq o'tkazish shart — Go o'zgaruvchilar orasida hech qachon yashirin o'tkazmaydi
Go turli sonli tiplardagi o'zgaruvchilar orasidagi yashirin o'tkazishlarga nihoyatda qattiq munosabatda: int ni float64 o'zgaruvchisiga float64(i) demasdan o'zlashtira olmaysiz.
Aynan shu qattiqqo'llik tipsiz konstantalarni shu qadar qimmatli qiladi. Ular sizga
Pi * radiusdeb yozish imkonini beradi — bundaradiusdasturingizning bir qismidafloat32, boshqasidafloat64bo'lishi mumkin — vaPitip tizimining boshqa joydagi qattiqqo'lligi xalaqit bermagan holda qaysi biri bo'lsa, o'shanga moslashadi.
const Pi = 3.14159
var r32 float32 = 5.0
area32 := Pi * r32 * r32 // bu yerda Pi float32 bo'ladi
var r64 float64 = 5.0
area64 := Pi * r64 * r64 // bu yerda esa float64
Konstantaga ochiq tip bersangiz, u shundan keyin aynan tipli o'zgaruvchi kabi tutadi — endi erkin moslashish yo'q:
const MaxUsers int = 100
var count int = MaxUsers // to'g'ri
var count32 int32 = MaxUsers // kompilyatsiya xatosi: tiplar mos emas
Amaliy qoida: aniq sabab bo'lmasa, konstantani tipsiz qoldiring. Matematik konstantalar, o'lchamlar va chindan bir nechta sonli kontekstda ishlatilishi mumkin bo'lgan sehrli sonlarni tipsiz qoldirgan deyarli har doim ma'qul. Tushuncha jihatidan bitta aniq tipga bog'langan konstanta (masalan, Status qiymati) esa tipli bo'lgani foydali — chunki aynan shu narsa uni ishlatilmasligi kerak bo'lgan joyda ishlatishga yo'l qo'ymaydi.
iota — faqat const bloklari ichida mavjud bo'lgan, oldindan e'lon qilingan identifikator. U 0 dan boshlab sanaydi va blokdagi har bir qator uchun bittaga oshadi:
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
Faqat birinchi qatorga = iota yozish kerak; blokdagi har bir keyingi qator iota bittaga oshirilgan holda xuddi shu ifodani takrorlaydi, shuning uchun Monday dan Saturday gacha hech narsa takrorlab yozilmaydi. iota har bir yangi const blokining boshida 0 ga qaytadi, shu sababli bir-biriga aloqasi yo'q ikki blok bir-birining raqamlashiga xalaqit bermaydi.
iota ustiga qurilgan bir nechta pattern shu qadar ko'p uchraydiki, ularni ko'rishi bilanoq tanib olish foydali. Nol qiymatni bo'sh identifikator bilan tashlab ketish — nol "belgilanmagan" yoki "yaroqsiz" ma'nosini bildirishi kerak bo'lganda foydali:
const (
_ = iota // 0 ni tashlab ketamiz
One // 1
Two // 2
)
Butun ketma-ketlikni siljitish uchun qo'shimchali ifoda:
const (
Red = iota + 1 // 1
Green // 2
Blue // 3
)
Yoki ikkining darajalarini hosil qilish uchun iota ni bit siljitish ifodasi ichida ishlating — bu ham bayt o'lchamlari, ham bit bayroqlari to'plamlari uchun standart idioma:
const (
KB = 1 << (10 * iota) // 1 << 0 = 1
MB // 1 << 10 = 1024
GB // 1 << 20
TB // 1 << 30
)
const (
Read = 1 << iota // 1 (ikkilikda 001)
Write // 2 (ikkilikda 010)
Execute // 4 (ikkilikda 100)
)
permissions := Read | Write // 3 — bit bayroqlari OR bilan birlashtiriladi, AND bilan tekshiriladi
Goda maxsus enum kalit so'zi yo'q, ammo aniqlangan butun son tipi va iota xuddi shu ishni bajaradi, hatto haqiqiy afzallik bilan: kompilyator sanab o'tish kutilgan joyga faqat o'sha tipdagi qiymatlar o'zlashtirilishini nazorat qiladi, ixtiyoriy int ni emas.
type Status int
const (
Pending Status = iota
Active
Completed
Cancelled
)
var s Status = Active // to'g'ri
// var s Status = 5 // kompilyatsiya bo'ladi, lekin 5 ning nomi yo'q — enum to'liq yozilmaganidan darak
Biroq sanab o'tish faqat o'qishga qulay bo'lsagina foydali, yalang'och Status qiymati esa ma'nosiz butun son bo'lib chiqadi. Idiomatik yechim — String() string metodi: u fmt.Println va shu kabilar fmt.Stringer interface'i orqali qiymatni tushunarli ko'rsatishiga imkon beradi:
func (s Status) String() string {
switch s {
case Pending:
return "pending"
case Active:
return "active"
case Completed:
return "completed"
case Cancelled:
return "cancelled"
default:
return fmt.Sprintf("Status(%d)", int(s))
}
}
Katta enum uchun bu String metodini qo'lda yozish tez zeriktiradi, aynan shuning uchun standart vositalar orasida stringer bor — u //go:generate stringer -type=Status izohi orqali chaqiriladigan kod generatori bo'lib, const blokidan shunday metodni avtomatik hosil qiladi. Kod generatsiyasini 51-bob batafsilroq yoritadi. Agar qiymatlar chindan butun son emas, satr bo'lishi kerak bo'lsa — masalan, JSON'ga matn sifatida to'g'ridan-to'g'ri saqlansa — asosiy tipni string deb e'lon qiling va String metodini butunlay tashlab ketavering:
type Color string
const (
ColorRed Color = "red"
ColorGreen Color = "green"
ColorBlue Color = "blue"
)
Ikkala yondashuv ham haqiqiy Go kodida doimo uchraydi.
Butun son va
Stringershakli ixchamroq hamda solishtirishda biroz tezroq, satr shakli esa saqlashda va nosozliklarni izlashda tushunarliroq — bu murosani odat bo'yicha emas, ataylab tanlash kerak.
Literallar — bu siz manba kodiga bevosita yozadigan haqiqiy qiymatlar, va Goning ular uchun sintaksisi ilk qarashda tuyulganidan kengroq. Butun son literallari o'nlik, o'n oltilik (0x), sakkizlik (0o) va ikkilik (0b) shakllarni qabul qiladi, hamda — kichik, ammo chindan foydali o'qish qulayligi — kompilyator shunchaki e'tiborsiz qoldiradigan pastki chiziqlarni raqam ajratuvchi sifatida qo'llaydi:
42
0x2A // o'n oltilikda 42
0o52 // sakkizlikda 42
0b101010 // ikkilikda 42
1_000_000 // 1 000 000, bir qarashda o'qish osonroq
Suzuvchi nuqtali literallar boshidagi nuqtali qisqartma va ilmiy yozuvni qo'llab-quvvatlaydi:
3.14
.5 // 0.5
1.5e10 // 1.5 × 10^10
1e-5 // 0.00001
Satr literallari ikki ko'rinishda bo'ladi:
\n, \t, \", \\, o'n oltilik bayt uchun \xNN, Unicode kod nuqtasi uchun \uNNNN) qayta ishlaydigan odatiy qo'shtirnoqli talqin qilinadigan satrlar (interpreted string literals)."line 1\nline 2"
`line 1
line 2`
Yakka tirnoq ichiga olingan rune literallari ('A', '世') bitta Unicode kod nuqtasini bildiradi va satrlardagi kabi escape belgilarini qabul qiladi:
'A'
'世'
'\n'
Mantiqiy literallar esa faqat true va false qiymatlarini oladi. Boshqa hech qanday tipdan avtomatik mantiqiy o'tkazish yo'q — Go hech qachon 0 yoki bo'sh satr "" ni C yoki Python kabi "yolg'on" (falsy) deb qabul qilmaydi.
O'zaro bog'liq konstantalarni alohida-alohida sochib yubormasdan, bitta umumiy const (...) blokiga guruhlang. Ularni tushunarsiz qisqartirish o'rniga to'liq ma'nosi bo'yicha nomlang (MR emas, balki MaxRetries). Nomining o'zidan maqsadi darhol ravshan bo'lmagan barcha eksport qilinadigan (katta harf bilan boshlangan) konstantalarga albatta izoh yozing.
Qiymatlaringiz mantiqan ketma-ketlik yoki bit bayrog'i (bitmask) shaklida bo'lsagina iota dan foydalaning; ketma-ket bo'lmagan yoki tasodifiy qiymatlarga uni majburan qo'llamang — bunday o'rinlarda aniq qiymatli literallar ancha tushunarlidir.
Konstantalar siz Go tilida yozilgan kodlarni o'qiyotganda tipsiz konstantalar qanchalik tez-tez va sezilmasdan o'z vazifasini bajarayotganini payqamaguningizcha kichik mavzudek tuyuladi: dasturdagi har bir
100yoki3.14soni, texnik jihatdan, moslashuvchan tipsiz konstanta hisoblanadi.
Endi esa bevosita o'zgaruvchilarning o'ziga o'tishimiz mumkin: ularning ko'rinish doirasi (scope), xotirada yashash sohasi va biror blok ichida tashqi o'zgaruvchilarni yashirib qo'yish (shadowing) xavflari haqida 12-bobda so'z yuritamiz.