11 / 73

Konstantalar va literallar

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 * someFloat32 shunchaki ishlashini tushuntiradi, holbuki tiplari mos kelmaydigan ikkita oddiy o'zgaruvchi bilan yozilgan xuddi shu ifoda kompilyatsiya bo'lishdan bosh tortadi.

Konstantalarni e'lon qilish

const kompilyatsiya paytida qat'iy belgilanadigan va hech qachon qayta o'zlashtirib bo'lmaydigan qiymatni e'lon qiladi:

example.go
const Pi = 3.14159
const Greeting = "Hello, World!"
const MaxUsers = 100

O'zaro bog'liq konstantalar bitta blokka guruhlansa, yaxshiroq o'qiladi:

example.go
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:

example.go
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.

Asosiy imkoniyat: tipli va tipsiz konstantalar

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.

example.go
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:

example.go
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 * radius deb yozish imkonini beradi — bunda radius dasturingizning bir qismida float32, boshqasida float64 bo'lishi mumkin — va Pi tip tizimining boshqa joydagi qattiqqo'lligi xalaqit bermagan holda qaysi biri bo'lsa, o'shanga moslashadi.

example.go
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:

example.go
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: ketma-ketlikni qo'lda yozmasdan hosil qilish

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:

example.go
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:

example.go
const (
    _   = iota // 0 ni tashlab ketamiz
    One        // 1
    Two        // 2
)

Butun ketma-ketlikni siljitish uchun qo'shimchali ifoda:

example.go
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:

example.go
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

Sanab o'tiladigan qiymatlar (enum) yaratish

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.

example.go
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:

example.go
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:

example.go
type Color string

const (
    ColorRed   Color = "red"
    ColorGreen Color = "green"
    ColorBlue  Color = "blue"
)

Ikkala yondashuv ham haqiqiy Go kodida doimo uchraydi.

Butun son va Stringer shakli ixchamroq hamda solishtirishda biroz tezroq, satr shakli esa saqlashda va nosozliklarni izlashda tushunarliroq — bu murosani odat bo'yicha emas, ataylab tanlash kerak.

Literallar

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:

example.go
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:

example.go
3.14
.5      // 0.5
1.5e10  // 1.5 × 10^10
1e-5    // 0.00001

Satr literallari ikki ko'rinishda bo'ladi:

  1. Maxsus escape-ketma-ketliklarni (\n, \t, \", \\, o'n oltilik bayt uchun \xNN, Unicode kod nuqtasi uchun \uNNNN) qayta ishlaydigan odatiy qo'shtirnoqli talqin qilinadigan satrlar (interpreted string literals).
  2. Escape-ketma-ketliklarni umuman qayta ishlamaydigan, kiritilgan har bir belgini o'z holicha saqlaydigan va bir necha qatorga bemalol cho'zila oladigan teskari tirnoqli xom satrlar (raw string literals — backtick bilan yoziladi). Ular regular expression (muntazam ifodalar), SQL so'rovlari yoki JSON shablonlari uchun eng tabiiy tanlovdir:
example.go
"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:

example.go
'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.

Tavsiya etiladigan amaliyotlar

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 100 yoki 3.14 soni, 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.