Goda metodlar
Metod — ma'lum bir turga bog'langan funksiya.
Oddiy funksiya alohida ishlaydi. Metod esa ma'lum bir qiymat yoki tur ustida amal bajaradi.
Masalan, bizda Rectangle turi bo'lsa, uning yuzasini hisoblash uchun Area() metodini yozishimiz mumkin:
Bu yerda Area() aynan Rectangle turiga tegishli metod hisoblanadi.
Metodning oddiy funksiyadan asosiy farqi — funksiya nomidan oldin qabul qiluvchi (receiver) yoziladi.
Masalan:
Bu yerda:
Area— metod nomi;Rectangle— metod bog'langan tur;r— receiver, ya'ni metod ishlayotgan qiymat.
Receiver yordamida metod qaysi turga tegishli ekanini bilamiz.
Value receiver
Avval oddiy misolni ko'ramiz:
package main
import "fmt"
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
rectangle := Rectangle{Width: 5, Height: 3}
fmt.Println("Yuza:", rectangle.Area())
}
Natija:
Bu kodda:
qismidagi:
receiver hisoblanadi.
Bu metod Rectangle turiga tegishli ekanini bildiradi.
Shuning uchun metodni quyidagicha chaqira olamiz:
Bu yerda rectangle qiymati metod ichidagi r receiveriga uzatiladi.
Lekin muhim jihat bor: Rectangle pointer emas.
Bu value receiver deyiladi.
Value receiver ishlatilganda metod receiver qiymatining nusxasi bilan ishlaydi.
Masalan:
Area() faqat Width va Height qiymatlarini o'qiyapti. U Rectangle ichidagi ma'lumotlarni o'zgartirmayapti.
Shuning uchun bu yerda value receiver ishlatish qulay.
Odatda value receiver quyidagi holatlarda mos keladi:
- metod qiymatni faqat o'qisa;
- qiymatni o'zgartirish kerak bo'lmasa;
- tur kichik bo'lsa;
- turni nusxalash katta xarajat talab qilmasa.
Metodni istalgan turga biriktirib bo'lmaydi.
Go'da metod faqat shu paketning o'zida e'lon qilingan nomlangan turga biriktirilishi mumkin.
Masalan, boshqa paketda joylashgan:
turiga o'z paketimizdan turib yangi metod qo'sha olmaymiz.
Ya'ni bunday qilish mumkin emas:
Sababi time.Time turi bizning paketimizda e'lon qilinmagan.
Oddiy turga metod qo'shish
Metod faqat struct uchun yozilmaydi.
O'z paketimizda e'lon qilingan boshqa nomlangan turlarga ham metod biriktirish mumkin.
Masalan:
package main
import "fmt"
type Celsius float64
func (c Celsius) Fahrenheit() float64 {
return float64(c)*9/5 + 32
}
func main() {
temperature := Celsius(25)
fmt.Println(temperature.Fahrenheit())
}
Natija:
Bu yerda:
orqali yangi Celsius turi yaratildi.
Uning asosida float64 yotadi, lekin Celsius Go uchun alohida nomlangan tur hisoblanadi.
Shuning uchun unga metod qo'shish mumkin:
Endi Celsius qiymatida:
deb metod chaqira olamiz.
Bu juda qulay.
Masalan, oddiy:
qiymati faqat son ekanini bildiradi.
Lekin:
deb alohida tur yaratish orqali bu qiymat harorat ekanini ham ifodaladik.
Bundan tashqari, unga harorat bilan bog'liq metodlarni ham qo'shishimiz mumkin.
Lekin quyidagi yozuv bilan adashtirmaslik kerak:
Bu yangi tur yaratmaydi.
Bu faqat float64 uchun boshqa nom, ya'ni alias yaratadi.
Shuning uchun aliasga alohida metod biriktirib bo'lmaydi.
Farq:
yangi tur yaratadi.
esa faqat float64 uchun boshqa nom yaratadi.
Pointer receiver
Ba'zi metodlar qiymatni faqat o'qimaydi. Ular asl qiymatni o'zgartirishi kerak bo'ladi.
Bunday holatda pointer receiver ishlatiladi.
Masalan:
package main
import "fmt"
type Account struct {
Balance int64
}
func (a *Account) Deposit(amount int64) error {
if a == nil {
return fmt.Errorf("account nil")
}
if amount <= 0 {
return fmt.Errorf("amount musbat bo'lishi kerak")
}
a.Balance += amount
return nil
}
func main() {
account := Account{Balance: 100_000}
if err := account.Deposit(25_000); err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println(account.Balance)
}
Natija:
Bu yerda metod receiveriga e'tibor bering:
Receiver:
ko'rinishida yozilgan.
*Account bu Account qiymatiga pointer degani.
Demak, Deposit() metodiga Account qiymatining nusxasi emas, uning manzili bilan bog'liq pointer beriladi.
Shuning uchun metod asl Account qiymatini o'zgartira oladi:
Dastlab balans:
edi.
Metod chaqirildi:
Natijada asl account ichidagi balans o'zgardi:
Agar bu metod value receiver bilan yozilganida:
a faqat accountning nusxasi bo'lar edi.
Metod ichidagi o'zgarish asl account qiymatiga ta'sir qilmas edi.
Pointer receiverda yana bir qulaylik bor.
Biz:
deb yozdik.
Aslida a pointer:
turida.
Nazariy jihatdan maydonga quyidagicha murojaat qilish mumkin:
Lekin Go buni avtomatik bajaradi.
Shuning uchun:
deb yozishning o'zi yetarli.
Go pointer orqali struct maydoniga murojaat qilayotganimizni tushunadi.
Yana bir qiziq joy:
Bu yerda account pointer emas.
U oddiy:
qiymati.
Lekin metod:
receiver talab qilyapti.
Go ko'p holatda buni ham avtomatik hal qiladi.
Agar qiymatning manzilini olish mumkin bo'lsa:
chaqiruvi amalda quyidagiga moslashtiriladi:
Shuning uchun har safar qo'lda &account yozish shart emas.
nil pointer receiver
Pointer receiverning yana bir xususiyati bor: receiver nil bo'lishi mumkin.
Masalan:
Bu yerda account:
qiymatga ega.
Shunga qaramasdan pointer receiverli metodni chaqirish texnik jihatdan mumkin:
Shuning uchun metod ichida kerak bo'lsa nilni tekshirish kerak:
Yuqoridagi Deposit() metodida aynan shunday qilingan.
Agar receiver nil bo'lsa, metod struct maydonlariga murojaat qilmaydi va xato qaytaradi.
Bu dasturda nil pointer dereference kabi runtime xatoning oldini oladi.
Metod orqali holatni boshqarish
Metodlarning muhim foydalaridan biri — ma'lumotni o'zgartirish qoidalarini bitta joyda saqlash.
Masalan, bank hisobini tasavvur qilamiz.
Biz balansni quyidagicha ochiq saqlashimiz mumkin:
Bunday holatda tashqi kod istalgan qiymatni yozishi mumkin:
Bu esa biznes qoidalariga mos kelmasligi mumkin.
Buning o'rniga maydonni paket tashqarisidan yopib, uni metodlar orqali boshqarish mumkin:
package main
import (
"errors"
"fmt"
)
type Account struct {
balance int64
}
func (a *Account) Deposit(amount int64) error {
if amount <= 0 {
return errors.New("summa musbat bo'lishi kerak")
}
a.balance += amount
return nil
}
func (a Account) Balance() int64 {
return a.balance
}
func main() {
account := Account{}
if err := account.Deposit(50_000); err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println(account.Balance())
}
Natija:
Bu yerda:
kichik harf bilan boshlangan.
Go'da kichik harf bilan boshlangan nomlar paket tashqarisiga export qilinmaydi.
Demak, boshqa paketdagi kod:
deb qiymatga bevosita murojaat qila olmaydi.
Balansni o'zgartirish uchun:
metodidan foydalanish kerak.
Metod ichida esa tekshiruv bor:
Shuning uchun noto'g'ri qiymat kiritilishining oldini bitta joyda olish mumkin.
Balansni o'qish uchun esa:
metodi ishlatiladi.
Natijada:
- qiymatni qanday o'zgartirish kerakligi
Deposit()ichida saqlanadi; - qiymatni qanday olish kerakligi
Balance()orqali belgilanadi; - biznes qoidalarini dasturning turli joylarida takrorlash kerak bo'lmaydi.
Bu yondashuv kodni boshqarishni osonlashtiradi.
Metodni chaqirish usullari
Metodlarni bir necha xil usulda ishlatish mumkin.
Eng oddiy usul — qiymat orqali metodni chaqirish.
Masalan:
Bu odatda eng ko'p ishlatiladigan ko'rinish.
Lekin Go metodni funksiya qiymati sifatida ham olish imkonini beradi.
Buning ikki muhim ko'rinishi bor:
- method value;
- method expression.
Method value
Quyidagi misolni ko'ramiz:
package main
import "fmt"
type Counter struct {
value int
}
func (c *Counter) Add(n int) {
c.value += n
}
func main() {
counter := &Counter{}
add := counter.Add
add(3)
add(2)
fmt.Println(counter.value)
}
Natija:
Oddiy holatda metodni:
deb chaqirishimiz mumkin.
Lekin bu misolda:
deb metodni o'zgaruvchiga saqladik.
Bu method value deyiladi.
Muhim jihati shundaki, counter qiymati metod bilan birga saqlanadi.
Ya'ni:
o'zgaruvchisi qaysi Counter ustida ishlashi kerakligini allaqachon biladi.
Shuning uchun:
deb chaqirganda counterni yana argument sifatida berish shart emas.
Keyin:
chaqirilganda ham aynan shu counter o'zgartiriladi.
Natijada:
hosil bo'ladi.
Method expression
Method expression boshqacha ishlaydi.
Masalan:
Bu yerda:
metodni ma'lum bir counter qiymatiga bog'lamaydi.
Aksincha, metod oddiy funksiyaga o'xshash ko'rinishda olinadi.
Shuning uchun receiverni o'zimiz birinchi argument sifatida beramiz:
Bu yerda:
receiver.
esa Add() metodining n argumenti.
Method expression bir xil metodni turli qiymatlar bilan ishlatish kerak bo'lgan holatlarda foydali.
Masalan:
counter1 := &Counter{}
counter2 := &Counter{}
add := (*Counter).Add
add(counter1, 5)
add(counter2, 10)
Bu yerda bitta add funksiyasi ikki xil Counter bilan ishlatilmoqda.
Avtomatik manzil olish va ochish
Go metodlarni chaqirishda pointer bilan ishlashni biroz soddalashtiradi.
Masalan, quyidagi ikki chaqiruv ko'p holatda bir xil ishlaydi:
Deposit() pointer receiver bilan yozilgan deb tasavvur qilamiz:
Birinchi chaqiruvda:
account oddiy Account qiymati.
Metod esa:
receiver talab qiladi.
Lekin account o'zgaruvchisining manzilini olish mumkin.
Shuning uchun Go avtomatik ravishda:
ko'rinishiga moslashtiradi.
Natijada biz har safar:
deb yozishimiz shart emas.
Oddiy:
yetarli.
Buning teskarisi ham mavjud.
Faraz qilamiz, Balance() value receiver bilan yozilgan:
Bizda esa pointer bor:
Shunga qaramay:
deb chaqirish mumkin.
Go pointer ichidagi qiymatni avtomatik ochadi va metodni chaqiradi.
Bu yerda Go biz uchun kerakli * yoki & amalini avtomatik bajarib beradi.
Lekin muhim bir narsani ajratish kerak:
Metod chaqirilayotganda Go'ning avtomatik
&yoki*ishlatishiAccountva*Accountturlarining metodlar to'plami bir xil degani emas.
Bu farq ayniqsa interface bilan ishlaganda muhim bo'ladi.
Receiver tanlash
Metod yozayotganda ko'p uchraydigan savollardan biri:
Value receiver ishlatish kerakmi yoki pointer receiver?
Bunga bitta universal qoida bilan javob berib bo'lmaydi.
Lekin bir nechta amaliy qoidalar bor.
Pointer receiver qachon ishlatiladi?
Quyidagi holatlarda pointer receiver tanlash odatda to'g'ri bo'ladi.
Metod qiymatni o'zgartirsa
Masalan:
Bu metod asl Account qiymatini o'zgartirishi kerak.
Shuning uchun pointer receiver kerak.
Struct katta bo'lsa
Value receiver ishlatilganda struct qiymati nusxalanadi.
Kichik structlar uchun bu odatda muammo emas.
Lekin struct juda katta bo'lsa, har bir metod chaqirig'ida uni nusxalash ortiqcha xarajat bo'lishi mumkin.
Bunday holatda pointer receiver ishlatish qulay.
Struct nusxalanmasligi kerak bo'lgan maydon saqlasa
Masalan:
kabi qiymatlarni ishlatayotgan structlarni nusxalash tavsiya etilmaydi.
Shuning uchun bunday turlarda odatda pointer receiver ishlatiladi.
Boshqa metodlar pointer receiver ishlatsa
Bir turda juda ko'p metodlar bo'lsa, receiver tanlashda izchillik foydali.
Masalan:
Bir metod pointer receiver, boshqasi value receiver bo'lib ketishi ba'zan kodni tushunishni qiyinlashtiradi.
Shuning uchun bir tur uchun receiver tanlovini imkon qadar izchil qilish yaxshi amaliyot.
Value receiver qachon ishlatiladi?
Value receiver kichik va qiymatga o'xshash turlar uchun qulay.
Masalan:
Bu qiymat kichik va uni o'zgartirish shart emas.
Shuning uchun:
value receiver uchun yaxshi misol.
Lekin value receiver haqida bitta muhim jihat bor.
Faraz qilamiz, struct ichida:
yoki:
bor.
Structning o'zi nusxalansa ham, map yoki slice ichidagi asosiy ma'lumot boshqa joyda saqlanishi mumkin.
Shuning uchun value receiver ishlatilgan metod ham map yoki slice ichidagi ma'lumotlarni o'zgartira olishi mumkin.
Masalan, faqat:
"Metod nimanidir o'zgartirsa pointer receiver ishlataman"
degan qoida har doim yetarli emas.
Receiver tanlashda turning qanday ma'lumot saqlayotgani va uning semantikasi ham hisobga olinadi.
Method set va interface bilan bog'lanish
Go'da har bir turda method set, ya'ni metodlar to'plami mavjud.
Bu ayniqsa interface bilan ishlaganda juda muhim.
Faraz qilamiz, bizda:
turi bor.
Agar metod value receiver bilan yozilsa:
bu metod Tning method setiga kiradi.
*T esa value receiver metodlarini ham ishlata oladi.
Ya'ni soddaroq qilib aytganda:
Tmethod setidaTreceiverli metodlar bor;*Tmethod setidaTva*Treceiverli metodlar bor.
Bu interface bajarilishiga ta'sir qiladi.
Masalan:
package main
import "fmt"
type Incrementer interface {
Increment()
}
type Counter int
func (c *Counter) Increment() {
*c++
}
func run(value Incrementer) {
value.Increment()
}
func main() {
var counter Counter
run(&counter)
fmt.Println(counter)
}
Natija:
Bu yerda interface:
Increment() metodiga ega bo'lgan tur kerakligini bildiradi.
Counter uchun metod quyidagicha yozilgan:
Receiver:
ekaniga e'tibor bering.
Demak, Increment() pointer receiverli metod.
Shuning uchun:
Incrementer interface'ni bajaradi.
Quyidagi kod ishlaydi:
Chunki:
turi:
bo'ladi.
Lekin:
kompilyatsiyadan o'tmaydi.
Sababi Counterning o'z method setida:
yo'q.
Metod faqat:
uchun e'lon qilingan.
Bu yerda yana bir muhim farqni eslab qolish kerak.
Oldingi bo'limda quyidagicha chaqiruv ishlashini ko'rdik:
Agar counterning manzilini olish mumkin bo'lsa, Go metod chaqirilayotganda avtomatik ravishda:
ko'rinishiga moslashtirishi mumkin.
Lekin interface tekshiruvida bunday avtomatik o'zgartirish amalga oshirilmaydi.
Interface'ga moslik turning method seti asosida kompilyatsiya vaqtida tekshiriladi.
Shuning uchun quyidagi ikki vaziyatni aralashtirmaslik kerak:
va:
Birinchisida Go metod chaqirig'ini avtomatik moslashtirishi mumkin.
Ikkinchisida esa Counter turi interface'ni bajaradimi yoki yo'qmi method set orqali tekshiriladi.
Receiver nomini tanlash
Receiver ham oddiy o'zgaruvchi kabi nomga ega bo'ladi.
Masalan:
Bu yerda receiver nomi:
Yoki:
bu yerda receiver:
Go kodida receiver nomi odatda tur nomining bir yoki ikki harfidan olinadi.
Masalan:
uchun:
ishlatilishi mumkin.
uchun:
ishlatilishi mumkin.
Go'da boshqa ayrim tillardagi kabi:
yoki:
nomlarini ishlatish odatiy emas.
Masalan, quyidagidan ko'ra:
quyidagi ko'rinish Go uslubiga yaqinroq:
Yana bir tavsiya — bir turga tegishli barcha metodlarda bir xil receiver nomidan foydalanish.
Masalan:
func (a *Account) Deposit(amount int64) {}
func (a *Account) Withdraw(amount int64) {}
func (a *Account) Balance() int64 {}
Bir metodda a, boshqasida acc, yana boshqasida account ishlatish mumkin, lekin bir xil nomdan foydalanish kodni tezroq o'qishga yordam beradi.
Misollar
1. Value receiver bilan hisoblash
package main
import "fmt"
type Tortburchak struct {
Eni float64
Boyi float64
}
func (t Tortburchak) Yuza() float64 {
return t.Eni * t.Boyi
}
func main() {
t := Tortburchak{
Eni: 8,
Boyi: 5,
}
fmt.Println(t.Yuza())
}
Bu misolda:
value receiver ishlatilgan.
Yuza() metodi Tortburchak ichidagi qiymatlarni o'zgartirmaydi.
U faqat:
va:
qiymatlarini o'qib, yuzani hisoblaydi.
Shuning uchun bu yerda pointer receiver kerak emas.
Metod chaqirilganda Tortburchak qiymati receiverga nusxa sifatida uzatiladi:
2. Pointer receiver bilan qiymatni yangilash
package main
import "fmt"
type Hisoblagich struct {
Qiymat int
}
func (h *Hisoblagich) Oshirish() {
h.Qiymat++
}
func main() {
h := Hisoblagich{}
h.Oshirish()
h.Oshirish()
fmt.Println(h.Qiymat)
}
Bu yerda Oshirish() metodining vazifasi Hisoblagich ichidagi qiymatni o'zgartirish.
Shuning uchun receiver:
ko'rinishida yozilgan.
Bu pointer receiver.
Metod ichidagi:
asl h qiymatini o'zgartiradi.
Birinchi chaqiruvdan keyin:
ikkinchi chaqiruvdan keyin:
bo'ladi.
Biz metodni:
deb chaqirdik.
Aslida metod *Hisoblagich receiver talab qiladi.
Lekin h o'zgaruvchisining manzilini olish mumkinligi uchun Go buni avtomatik ravishda:
ko'rinishiga moslashtiradi.
3. Oddiy nomlangan turga metod qo'shish
package main
import "fmt"
type Celsius float64
func (c Celsius) Fahrenheit() float64 {
return float64(c)*9/5 + 32
}
func main() {
harorat := Celsius(25)
fmt.Println(harorat.Fahrenheit())
}
Metod faqat struct uchun yozilishi shart emas.
Bu misolda:
orqali float64 asosida yangi Celsius turi yaratildi.
Endi Celsius uchun alohida metod yozish mumkin:
Metod Celsius qiymatini Fahrenheitga aylantiradi.
Shuning uchun:
ko'rinishidagi kod tushunarli o'qiladi.
Bu usul oddiy qiymatlarga ham ma'no va ularga tegishli xatti-harakatlarni biriktirish imkonini beradi.
4. Metod orqali qiymatni tekshirish
package main
import "fmt"
type Yosh int
func (y Yosh) Togri() bool {
return y >= 0 && y <= 150
}
func main() {
yosh := Yosh(24)
fmt.Println(yosh.Togri())
}
Bu misolda:
orqali yosh uchun alohida tur yaratildi.
Keyin yosh qiymati to'g'ri oraliqda ekanini tekshiradigan metod yozildi:
Endi dasturning har bir joyida:
deb tekshiruv yozish shart emas.
Buning o'rniga:
deb chaqirish mumkin.
Tekshiruv qoidasi turning o'z metodida saqlanadi.
Agar keyinchalik qoida o'zgarsa, faqat bitta metodni o'zgartirish kifoya qiladi.
5. Metod orqali boshqariladigan yangilanish
package main
import "fmt"
type Hisob struct {
Balans int
}
func (h *Hisob) Yechish(summa int) bool {
if summa <= 0 || summa > h.Balans {
return false
}
h.Balans -= summa
return true
}
func main() {
h := Hisob{Balans: 100}
fmt.Println(h.Yechish(30), h.Balans)
}
Bu misolda hisobdan pul yechish uchun Yechish() metodi yozilgan.
Metod avval berilgan summani tekshiradi:
Ikki holatda pul yechilmaydi:
- summa
0yoki undan kichik bo'lsa; - summa balansdan katta bo'lsa.
Agar qiymat to'g'ri bo'lsa:
orqali balans kamaytiriladi.
So'ng:
qaytariladi.
Shuning uchun metodning natijasidan operatsiya bajarilgan yoki bajarilmaganini bilish mumkin.
Eng muhim jihati — noto'g'ri summa berilsa, Hisob holati o'zgarmaydi.
6. nil receiverni tekshirish
package main
import "fmt"
type Tugun struct {
Qiymat int
}
func (t *Tugun) QiymatYokiNol() int {
if t == nil {
return 0
}
return t.Qiymat
}
func main() {
var tugun *Tugun
fmt.Println(tugun.QiymatYokiNol())
}
Bu misolda:
deb pointer e'lon qilingan.
Lekin unga hech qanday qiymat berilmagan.
Shuning uchun:
bo'ladi.
Shunga qaramay:
metodini chaqirish mumkin.
Sababi metod pointer receiver bilan yozilgan:
Metod ichida receiver avval tekshiriladi:
Shundan keyingina:
maydoniga murojaat qilinadi.
Agar nil tekshiruvisiz darhol:
deb yozilganida, nil pointer sabab runtime xatosi yuz berishi mumkin edi.
7. Method value
package main
import "fmt"
type Kopaytirgich int
func (k Kopaytirgich) Hisobla(son int) int {
return int(k) * son
}
func main() {
ikki := Kopaytirgich(2)
hisobla := ikki.Hisobla
fmt.Println(hisobla(7))
}
Bu yerda:
qiymati yaratildi.
Keyin:
deb metod o'zgaruvchiga saqlandi.
Bu method value hisoblanadi.
Muhim jihati shundaki, hisobla ichida ikki receiveri ham saqlanadi.
Shuning uchun:
chaqirilganda ikkini yana uzatish kerak emas.
Go uni quyidagi ma'noda ishlatadi:
ikkining qiymati 2, argument esa 7.
Natijada:
hosil bo'ladi.
8. Method expression
package main
import "fmt"
type Son int
func (s Son) Kvadrat() int {
return int(s * s)
}
func main() {
kvadrat := Son.Kvadrat
fmt.Println(kvadrat(Son(9)))
}
Bu safar:
deb metod olindi.
Bu method expression hisoblanadi.
Method valuedan farqli ravishda, bu yerda metod ma'lum bir receiver qiymatiga bog'lanmagan.
Shuning uchun receiver chaqiruv vaqtida birinchi argument sifatida beriladi:
Bu deyarli quyidagiga teng:
Method expression yordamida bitta metodni turli receiver qiymatlari bilan ishlatish mumkin.
Masalan:
Har bir chaqiruvda birinchi argument receiver sifatida ishlatiladi.
9. Slice elementlarida pointer receiver
package main
import "fmt"
type Mahsulot struct {
Narx int
}
func (m *Mahsulot) Chegirma(foiz int) {
m.Narx -= m.Narx * foiz / 100
}
func main() {
mahsulotlar := []Mahsulot{
{Narx: 100},
{Narx: 200},
}
for i := range mahsulotlar {
mahsulotlar[i].Chegirma(10)
}
fmt.Println(mahsulotlar)
}
Bu misolda Mahsulot uchun narxni o'zgartiradigan metod yozilgan:
Narx o'zgarishi kerakligi sabab pointer receiver ishlatilgan.
Slice:
ikki elementdan iborat.
Loop:
har bir elementga 10 foiz chegirma qo'llaydi.
Bu yerda muhim qism:
indeks orqali olinmoqda.
Slice elementi indeks orqali olinganda uning manzilini olish mumkin.
Shuning uchun Go:
chaqiruvini pointer receiver uchun moslashtira oladi.
Natijada slice ichidagi asl elementlarning Narx qiymati o'zgaradi.
Birinchi mahsulot:
dan:
ga tushadi.
Ikkinchi mahsulot:
dan:
ga tushadi.
10. Bir tur uchun izchil receiver tanlash
package main
import "fmt"
type Savat struct {
Soni int
}
func (s *Savat) Qosh(soni int) {
s.Soni += soni
}
func (s *Savat) Boshmi() bool {
return s.Soni == 0
}
func main() {
s := Savat{}
fmt.Println(s.Boshmi())
s.Qosh(3)
fmt.Println(s.Boshmi())
}
Bu misolda Savat uchun ikkita metod bor:
va:
Qosh() metodi savat holatini o'zgartiradi:
Shuning uchun bu metodda pointer receiver kerak.
Boshmi() esa faqat qiymatni tekshiradi:
Texnik jihatdan uni value receiver bilan ham yozish mumkin edi:
Lekin shu turdagi boshqa metodlar pointer receiver ishlatayotgani uchun Boshmi() ham pointer receiver bilan yozilgan:
Bu bir tur metodlarida receiver tanlovini izchil saqlaydi.
Natijada Savat bilan ishlaganda qaysi metod pointer, qaysi metod value receiver ishlatishini alohida eslab yurish kamroq kerak bo'ladi.