Shanraq.org Shanraq.org
Go-дағы сілтегіштер мен әдістер: түзету неге сақталмаған
IT

Go тілі: нөлден өз блогыңызға дейін 12-сабақ (барлығы 50)

Go-дағы сілтегіштер мен әдістер: түзету неге сақталмаған

Go курсының он екінші сабағы. Өткен сабақтағы тұзақтың жауабы: сілтегіш — мәннің көшірмесі емес, мекенжайы. & мен * қалай жұмыс істейді, әдістің функциядан айырмашылығы, қабылдағышты қашан сілтегіш, қашан мән алады, және for i := range неге for _, a := range бұзғанды түзетеді.

Не үшін керек

Өткен сабақта range ішіндегі түзету ізсіз жоғалды, ал компилятор үндемеді. Сонда біз айттық: нағыз шешім — сілтегіштер. Міне, ол.

Сілтегіш дәл сол жерде керек: бөтен мәннің көшірмесін емес, өзін өзгерту қажет болғанда — мақаланы жариялау, сөз қосу, тақырыпты түзету.

Бірден тұтас

Жаңа қалта, go mod init sabaq11, main.go:

package main

import "fmt"

type Article struct {
	Title     string
	Words     int
	Published bool
}

func (a Article) ReadingTime() int {
	return (a.Words + 199) / 200
}

func (a *Article) Publish() {
	a.Published = true
}

func rename(a *Article, title string) {
	a.Title = title
}

func main() {
	first := Article{Title: "Дала туралы", Words: 400}

	fmt.Println(first.Title, "·", first.ReadingTime(), "мин ·", first.Published)

	first.Publish()
	rename(&first, "Дала туралы: толық нұсқа")
	fmt.Println(first.Title, "·", first.Published)

	blog := []Article{
		{Title: "Сәлем, әлем", Words: 150},
		{Title: "Шаңырақ деген не", Words: 1000},
	}

	for i := range blog {
		blog[i].Publish()
	}
	for _, a := range blog {
		fmt.Println(a.Title, "·", a.Published)
	}

	p := &first
	fmt.Printf("%T · %s\n", p, p.Title)
}

Іске қоспас бұрын — төменге қарамай, оның не басып шығаратынын дауыстап айтыңыз. Содан кейін жіберіп, салыстырыңыз.

Дала туралы · 2 мин · false
Дала туралы: толық нұсқа · true
Сәлем, әлем · true
Шаңырақ деген не · true
*main.Article · Дала туралы: толық нұсқа

Талдау

Сілтегіш — заттың өзі емес, мекенжайы

p := &first        // мекенжайын алу
fmt.Println(*p)    // мекенжайға барып, мәнді алу

Мәннің алдындағы & оның мекенжайын береді. Сілтегіштің алдындағы * мекенжай бойынша мәнге қайта апарады. Ал түр жариялауындағы сол * «пәленшенің мекенжайы» дегенді білдіреді: *Article — «мақаланың мекенжайы», мақаланың өзі емес.

%T мұны растады: p-ның түрі — жұлдызшалы *main.Article.

Елестетіп көріңіз. Пәтердің кілті мен суреті. Суретін берді — үстінен қалағаныңызды сызыңыз, пәтерде ештеңе жылжымайды. Кілтін берді — барып, жиһазды шын мәнінде қайта қоюға болады. Сілтегіш — сол кілт.

Көшірме арзан әрі қауіпсіз, сондықтан Go әдепкіде көшіреді. Кілтті өзгерту керек болғанда береді.

Сілтегіш алатын функция түпнұсқаны өзгертеді

func rename(a *Article, title string) {
	a.Title = title
}

rename(&first, "Дала туралы: толық нұсқа")

Параметр *Article деп жарияланған, ал шақырғанда біз &first — мекенжай береміз. Тақырып сырттан өзгерді, мұны шығыстың екінші жолынан көріп отырмыз.

Өткен сабақпен салыстырыңыз: онда функция Article қабылдап, көшірме алатын да, түзетуі ешқайда жетпейтін.

Әдіс — типке жазылып қойылған функция

func (a Article) ReadingTime() int {

Аттың алдындағы жақша — қабылдағыш. Ол былай дейді: функция Article типіне тиесілі, оны нүкте арқылы шақыру керек — ReadingTime(first) емес, first.ReadingTime().

Елестетіп көріңіз. Заттың өзіне басылған нұсқаулық. Кір жуғыш машинаның режимдері бөлек іздейтін кітапта емес, панельдің өзінде жазылған. Әдіс те сондай: шеберлік типтің үстінде жатыр.

Қабылдағыш: мән әлде сілтегіш

Сабақтың бүкіл ережесі екі жолға сыяды.

func (a Article) ReadingTime() int   // тек оқиды — мән алыңыз
func (a *Article) Publish()          // өзгертеді — сілтегіш алыңыз

ReadingTime санайды да, ештеңеге тимейді: көшірме жеткілікті. Publish өріске жазады, ал сілтегішсіз жазу көшірмеге кетер еді де, Published false болып қалар еді — сабақ басталған дәл сол тұзақ.

Болашаққа бір ескерту: типтің бір әдісі сілтегіш алса, қалғандарына да әдетте сілтегіш береді. Араластыруға болады, бірақ кодыңызды оқитын адам әр жолы қайсысы қандай әдіс екенін еске түсіруге мәжбүр болады.

Go жұлдызшаны өзі қояды

first — сілтегіш емес, кәдімгі мән. Сонда да first.Publish() жұмыс істейді. Керісінше де солай: p — сілтегіш, ал p.Title ешқандай жұлдызшасыз жазылады.

Go біржақты болғанда & мен *-ты өзі қояды. Сондықтан күнделікті кодта жұлдызша кездесе бермейді — тек түр жариялауларында.

Өткен сабақтың тұзағы қалай түзеледі

for i := range blog {
	blog[i].Publish()
}

Мұнда range тек нөмір береді. blog[i] — тілім элементінің көшірмесі емес, өзі, ал Go оның мекенжайын автоматты алады, себебі тілім элементі мекенжайланады.

Ал for _, a := range blog { a.Publish() } бұрынғысынша ештеңе істемейді: a — көшірме, көшірменің өз мекенжайы бар, жарияланатыны да сол. Компилятор мұнда да үндемейді.

Есте сақтау оңай: өзгертесіз бе — индекспен жүріңіз.

nil-сілтегіш

Сілтегіш ешқайда сілтемеуі мүмкін. Оның нөлдік мәні — nil:

var p *Article
fmt.Println(p.Title)   // panic: invalid memory address or nil pointer dereference

Елестетіп көріңіз. Қағаздағы мекенжай, бірақ онда ештеңе салынбаған. Бардыңыз — бос алаң.

Бұл сирек те, апат та емес: хабар не болғанын тура айтады. Қателер туралы сабақта мұндай жағдайды адамша қайтаруды үйренеміз.

Сабақ картасы

Сабақ картасы: көшірме тұйыққа кетеді, мекенжай мәнге қайта әкеледі

Өз сөзіңізбен айтыңыз

Қарамай, дауыстап немесе қағазға жазып жауап беріңіз. Жауаптары — сабақтың соңында.

  1. func (a Article) ReadingTime() пен func (a *Article) Publish() айырмасы неде және екеуінің қайсысын қалай таңдайды?
  2. first — сілтегіш емес, бірақ first.Publish() жұмыс істейді. Неге?
  3. for _, a := range blog { a.Publish() } ештеңе жарияламайды, ал for i := range blog { blog[i].Publish() } жариялайды. Айырмасы неде?

Тапсырма

Міндетті. Article типіне Shorten(limit int) әдісін қосыңыз — тақырыпты limit әріпке дейін қысқартып, соңына көп нүкте қояды. Байт емес, әріп — руналар сабағындағыдай. Оны тілімдегі барлық мақалаға қолданып, нәтижесін басып шығарыңыз. Жазар алдында қабылдағыш қандай болу керегін ойлаңыз.

Қалауыңызша.

  • Shorten-ді for _, a := range blog циклінде шақырып, ештеңе өзгермегеніне көз жеткізіңіз.
  • first пен &first үшін %T басып шығарыңыз.
  • publishAll(blog []Article) функциясын жазып, жұмыс істейтінін тексеріңіз. Істемесе — неге, және мұны жариялаудағы бір таңбамен қалай түзетуге болады.

Блогта қайда тұрады

Әрі қарай бәрі дерлік сілтегішпен болады: өңдеуші *http.Request алады — бұл жұлдызшаны сіз ең алғашқы серверде көрдіңіз де, — дерекқор *sql.Rows береді, үлгі *template.Template қабылдайды. Енді оның мұндағы мәні түсінікті: сізге сурет емес, кілт беріліп тұр.

Жауаптар

Жауаптарды көрсету
  1. Біріншісі өрістерді тек оқиды және көшірме алады — соның өзі жеткілікті. Екіншісі өріске жазады, ал сілтегішсіз жазу сол сәтте жоғалатын көшірмеге кетер еді. Ережесі: өзгертсе — сілтегіш, тек оқитын болса — мән.
  2. Себебі мән мекенжайланатын болса, Go &-ты өзі қояды. first — айнымалы, оның мекенжайы бар, сондықтан шақыру (&first).Publish() болып шығады. Жұлдызша мен амперсандты қолмен жазу сирек керек болады.
  3. Бірінші жағдайда a — элементтің көшірмесі; әдіс көшірмені жариялайды, ал ол келесі айналымда жоғалады. Екіншісінде blog[i] — тілім элементінің өзі, оның мекенжайы бар, сондықтан сілтегіш алатын әдіс тілімде жатқанды өзгертеді.

Дереккөздер

Мәтінде қате не теру қатесі кездессе, бізге айтыңыз

Тапсырманы тексеру

Алдымен VS Code-та шешіп, іске қосыңыз — редактор қатені сол жерде көрсетеді. Дайын шешімді осында қойыңыз. Тексеретін — модель: ол қатені атап көрсетеді, бірақ дайын жауапты бермейді.

Тексеру үшін кіру керек. Кіру

Пікірлер (0)

Әзірге пікір жоқ. Бірінші болыңыз.