Tarkibga o'tish

Go’da enum va iota

Go’da alohida enum kalit so‘zi yo‘q. Enumga o‘xshash qiymatlar nomlangan tur va konstantalar bilan yaratiladi. iota esa const guruhi ichida ketma-ket sonlarni yozishni ixchamlashtiradi.

Nomlangan tur va konstantalar

package main

import "fmt"

type Status int

const (
    StatusUnknown Status = iota
    StatusPending
    StatusProcessing
    StatusCompleted
)

func main() {
    status := StatusProcessing
    fmt.Println(status)
    fmt.Println(status == StatusCompleted)
}

Natija:

2
false

Status nomlangan tur bo‘lgani uchun koddagi ma’no oddiy intdan aniqroq. StatusUnknownni nol qiymatga qo‘yish foydali: var status Status tasodifan haqiqiy biznes holatini bildirmaydi.

iota qanday ishlaydi?

iota har bir const guruhida 0 dan boshlanadi va har bir ConstSpec satrida bittaga oshadi. Ifoda yozilmagan satr oldingi ifoda va uning turini takrorlaydi:

const (
    A = iota + 1 // 1
    B            // 2
    C            // 3
)

Bir satrda bir nechta konstanta bo‘lsa, ular uchun iota bir xil bo‘ladi. _ orqali ayrim qiymatni tashlab ketish mumkin:

type Size uint64

const (
    _  Size = 1 << (10 * iota)
    KB
    MB
    GB
)

Bu yerda birinchi satr _ga beriladi, keyingi qiymatlar mos ravishda 1024, 1048576 va 1073741824 bo‘ladi.

Matn ko‘rinishini berish

Nomlangan turga String metodi qo‘shilsa, fmt uni o‘qilishi qulay ko‘rinishda chiqaradi:

package main

import "fmt"

type Status int

const (
    StatusUnknown Status = iota
    StatusPending
    StatusProcessing
    StatusCompleted
)

func (s Status) String() string {
    switch s {
    case StatusUnknown:
        return "unknown"
    case StatusPending:
        return "pending"
    case StatusProcessing:
        return "processing"
    case StatusCompleted:
        return "completed"
    default:
        return fmt.Sprintf("Status(%d)", s)
    }
}

func main() {
    fmt.Println(StatusProcessing)
    fmt.Println(Status(99))
}

Natija:

processing
Status(99)

default noma’lum qiymatni yashirmaydi. Bu log va diagnostikada foydali. Katta enumlar uchun String metodini generatsiya qiluvchi vositalar ishlatilishi mumkin, ammo generatsiya natijasi repozitoriy qoidalariga muvofiq boshqariladi.

Enum yopiq to‘plam emas

Muhim farq: nomlangan son turiga istalgan mos sonni ochiq konvertatsiya qilish mumkin — Status(99) kompilyatsiyadan o‘tadi. Demak, Go’dagi bu usul qiymatlarni mutlaq cheklamaydi. Tashqi input, JSON yoki database qiymatini qabul qilganda validatsiya kerak:

func (s Status) Valid() bool {
    return s >= StatusUnknown && s <= StatusCompleted
}

Konstantalarni database yoki tarmoq protokolida saqlasangiz, o‘rtaga yangi iota qiymati qo‘shish keyingi raqamlarni siljitadi. Bunday formatlarda aniq sonlarni yozish yoki faqat oxiriga yangi qiymat qo‘shish xavfsizroq.

Bit bayroqlar

iota bitmask yaratishda ham ishlatiladi:

type Permission uint8

const (
    PermissionRead Permission = 1 << iota
    PermissionWrite
    PermissionDelete
)

func has(value, permission Permission) bool {
    return value&permission != 0
}

Bu oddiy ketma-ket enum emas: qiymatlar 1, 2, 4 bo‘lib, | bilan birlashtiriladi. Masalan, PermissionRead | PermissionWrite ikkala ruxsatni saqlaydi.

Xulosa

  • Go’da enum alohida til konstruksiyasi emas; nomlangan tur va konstantalar ishlatiladi.
  • iota const guruhidagi satrlar bo‘yicha oshadi va har yangi guruhda nolga qaytadi.
  • Nol qiymatni Unknown holatiga ajratish xavfsiz dizayn beradi.
  • Istalgan sonni nomlangan turga konvertatsiya qilish mumkin, shu sabab tashqi qiymat validatsiya qilinadi.
  • Barqaror tashqi formatlarda iota raqamlarining siljishiga ehtiyot bo‘lish kerak.

Misollar

1. Hafta kunlarini birdan boshlash

Bu misolda iota + 1 yordamida hafta kunlariga 1 dan boshlab qiymat beriladi.

package main

import "fmt"

type Weekday int

const (
    Monday Weekday = iota + 1
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
)

func main() {
    fmt.Println(Monday, Wednesday, Sunday)
}

iota odatda 0 dan boshlanadi. + 1 birinchi qiymatni 1 qiladi, keyingi satrlar esa 2, 3 va shu tartibda davom etadi. Bu raqamlarni foydalanuvchi ko‘radigan hafta kunlari bilan moslashtirishga yordam beradi.

2. Nol qiymatni noma’lum holat uchun saqlash

Bu misolda tur e’lon qilinganda olinadigan nol qiymat alohida Unknown holatini bildiradi.

package main

import "fmt"

type DeliveryStatus int

const (
    DeliveryUnknown DeliveryStatus = iota
    DeliveryAccepted
    DeliveryOnTheWay
    DeliveryArrived
)

func main() {
    var status DeliveryStatus

    fmt.Println(status == DeliveryUnknown)
    status = DeliveryOnTheWay
    fmt.Println(status)
}

statusga boshlang‘ich qiymat berilmagani uchun u 0 bo‘ladi. DeliveryUnknown ham 0 bo‘lgani sababli hali holat tanlanmagani aniq ko‘rinadi. Haqiqiy jarayon holatlari 1 dan boshlanadi.

3. Keraksiz birinchi qiymatni tashlab ketish

Bu misolda _ birinchi iota qiymatini nom bermasdan o‘tkazib yuboradi.

package main

import "fmt"

type Priority int

const (
    _ Priority = iota
    PriorityLow
    PriorityMedium
    PriorityHigh
)

func main() {
    fmt.Println(PriorityLow, PriorityMedium, PriorityHigh)
}

Birinchi satrda iota 0 ga teng, lekin qiymat _ga yozilgani uchun undan foydalanib bo‘lmaydi. Natijada darajalar 1, 2 va 3 qiymatlarini oladi. Bu 0 haqiqiy daraja bo‘lmasligi kerak bo‘lgan holatda qulay.

4. Qiymatlarni o‘n qadam bilan oshirish

Bu misolda iota formula ichida ishlatilib, bo‘lim kodlari orasida joy qoldiriladi.

package main

import "fmt"

type DepartmentCode int

const (
    DepartmentSales DepartmentCode = (iota + 1) * 10
    DepartmentSupport
    DepartmentWarehouse
)

func main() {
    fmt.Println(DepartmentSales, DepartmentSupport, DepartmentWarehouse)
}

Birinchi satrda iota 0, shuning uchun (0 + 1) * 10 natijasi 10 bo‘ladi. Keyingi qiymatlar 20 va 30. Formuladagi + 1 birinchi kodning 0 bo‘lib qolishiga yo‘l qo‘ymaydi.

5. Bir satrda bog‘liq qiymatlar yaratish

Bu misolda bitta iota qiymatidan diapazonning boshi va oxiri hisoblanadi.

package main

import "fmt"

const (
    SmallMin, SmallMax = iota * 10, iota*10 + 9
    MediumMin, MediumMax
    LargeMin, LargeMax
)

func main() {
    fmt.Println(SmallMin, SmallMax)
    fmt.Println(MediumMin, MediumMax)
    fmt.Println(LargeMin, LargeMax)
}

Bir ConstSpec satridagi ikkala iota ham bir xil qiymatga ega. Shu sabab diapazonlar 0–9, 10–19 va 20–29 ko‘rinishida hosil bo‘ladi. Keyingi satrlarda ifodani qayta yozish shart emas.

6. Har bir const guruhida qayta boshlash

Bu misol iota yangi const guruhi ochilganda yana 0 dan boshlanishini ko‘rsatadi.

package main

import "fmt"

const (
    North = iota
    East
)

const (
    Draft = iota
    Published
)

func main() {
    fmt.Println(North, East)
    fmt.Println(Draft, Published)
}

Birinchi guruhdagi qiymatlar 0 va 1. Ikkinchi guruh avvalgisini davom ettirmaydi: Draft yana 0, Published esa 1 bo‘ladi. Alohida enumlar shu tariqa bir-biriga ta’sir qilmaydi.

7. Fayl ruxsatlarini birlashtirish

Bu misolda ikkilik bayroqlar | operatori bilan bitta qiymatda saqlanadi.

package main

import "fmt"

type FilePermission uint8

const (
    CanRead FilePermission = 1 << iota
    CanWrite
    CanExecute
)

func main() {
    permissions := CanRead | CanWrite

    fmt.Println(permissions)
    fmt.Println(permissions&CanRead != 0)
    fmt.Println(permissions&CanExecute != 0)
}

1 << iota qiymatlarni 1, 2 va 4 qiladi. Ularning bitlari ustma-ust tushmaydi. permissions&CanRead != 0 o‘qish biti yoqilganini, permissions&CanExecute != 0 esa bajarish biti yoqilmaganini tekshiradi.

8. Bayroqni qiymatdan olib tashlash

Bu misolda &^ operatori yoqilgan bayroqlardan bittasini o‘chiradi.

package main

import "fmt"

type Notification uint8

const (
    NotifyEmail Notification = 1 << iota
    NotifySMS
    NotifyPush
)

func main() {
    settings := NotifyEmail | NotifySMS | NotifyPush
    settings &^= NotifySMS

    fmt.Println(settings&NotifyEmail != 0)
    fmt.Println(settings&NotifySMS != 0)
    fmt.Println(settings&NotifyPush != 0)
}

Boshlanishida uchala bayroq ham | orqali yoqiladi. &^= o‘ng tomondagi NotifySMS bitini tozalaydi, qolgan bitlarni o‘zgartirmaydi. Shu sabab natijalar true, false, true bo‘ladi.

9. Enum qiymatlarini matnga aylantirish

Bu misolda switch asosidagi metod buyurtma holatini o‘qilishi qulay matn bilan qaytaradi.

package main

import "fmt"

type OrderStatus int

const (
    OrderNew OrderStatus = iota
    OrderPaid
    OrderSent
)

func (s OrderStatus) Label() string {
    switch s {
    case OrderNew:
        return "yangi"
    case OrderPaid:
        return "to‘langan"
    case OrderSent:
        return "yuborilgan"
    default:
        return "noma’lum"
    }
}

func main() {
    fmt.Println(OrderPaid.Label())
    fmt.Println(OrderStatus(20).Label())
}

Label() sonli qiymatni foydalanuvchiga tushunarli matnga o‘giradi. default tanilmagan son uchun ishlaydi. Chunki OrderStatus(20) kabi ochiq konvertatsiya enum ro‘yxatida bo‘lmagan qiymatni ham yaratishi mumkin.

10. Tashqi format uchun aniq kodlarni saqlash

Bu misolda tashqi tizim bilan almashiladigan kodlar iota o‘rniga aniq sonlar bilan beriladi.

package main

import "fmt"

type PaymentCode int

const (
    PaymentCreated  PaymentCode = 100
    PaymentApproved PaymentCode = 200
    PaymentRejected PaymentCode = 400
)

func main() {
    codes := []PaymentCode{PaymentCreated, PaymentApproved, PaymentRejected}

    for _, code := range codes {
        fmt.Println(code)
    }
}

Bu qiymatlar tashqi API yoki database bilan kelishilgan deb tasavvur qilinadi. Aniq son yozilgani uchun konstantalar orasiga yangi holat qo‘shish mavjud kodlarni siljitmaydi. Slice tartibi 100, 200, 400 qiymatlarini ketma-ket ko‘rsatish uchun tanlangan.