Shanraq.org Shanraq.org
Go-дағы қателер: ерекше жағдай емес, кәдімгі мән
IT

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

Go-дағы қателер: ерекше жағдай емес, кәдімгі мән

Go курсының он төртінші сабағы. Мұнда қате бағдарламаны үзбейді, екінші мән болып қайтады, ал тексерілуі бірінші. error — бір ғана әдісі бар интерфейс, errors.New мен fmt.Errorf, %w арқылы орау және errors.Is, өз қатеңізді мән ретінде жариялау — және мұның бәріне panic неге керек емес.

Не үшін керек

Оқырман /read/kokek бетін ашты, ал ондай мақала жоқ. Бұл бағдарламаның бұзылуы емес — бұл 404 кодымен жауап беру керек кәдімгі жағдай.

Go-да бұл үшін try де, catch та жоқ. Функция жай ғана екі мән қайтарады: нәтиже мен қате. Сіз екіншісіне қарап, не істеу керегін шешесіз.

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

Бірден тұтас

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

package main

import (
	"errors"
	"fmt"
)

type Article struct {
	Title string
	Words int
}

var ErrNotFound = errors.New("мақала табылмады")

type Store struct {
	items map[string]Article
}

func (s *Store) Get(slug string) (Article, error) {
	a, ok := s.items[slug]
	if !ok {
		return Article{}, fmt.Errorf("get %q: %w", slug, ErrNotFound)
	}
	return a, nil
}

func show(s *Store, slug string) {
	a, err := s.Get(slug)
	if err != nil {
		if errors.Is(err, ErrNotFound) {
			fmt.Println("404:", err)
			return
		}
		fmt.Println("500:", err)
		return
	}
	fmt.Println(a.Title, "·", a.Words, "сөз")
}

func main() {
	s := &Store{items: map[string]Article{
		"dala": {Title: "Дала туралы", Words: 400},
	}}

	show(s, "dala")
	show(s, "kokek")

	_, err := s.Get("kokek")
	fmt.Println(errors.Is(err, ErrNotFound))
	fmt.Println(errors.Unwrap(err) == ErrNotFound)
}

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

Дала туралы · 400 сөз
404: get "kokek": мақала табылмады
true
true

Талдау

error — бір әдісті интерфейс

Кешегі сабақ бүгінгіні дерлік айқын етіп қойды. error — тілдің ерекше құрылымы емес, бір әдістен тұратын кәдімгі интерфейс:

type error interface {
	Error() string
}

Error() string-ті білетіннің бәрі қатеге жарайды. Ешқандай кластар иерархиясы да, «ерекше жағдай типтері» де жоқ — бір әдісі бар тип қана.

Функция екі мән қайтарады

func (s *Store) Get(slug string) (Article, error)

Сол жақта нәтиже, оң жақта қате — Go-да бұл рет әрқашан сақталады. Қате болмаса, екінші болып nil келеді, ал бірінші мән мағыналы. Қате болса, бірінші мән әдетте нөлдік, оған сүйенуге болмайды.

Осыдан бөтен кодты оқу ережесі шығады: екі нәтижелі шақыруды көрдіңіз бе — көзіңізбен екіншісінің тексерілуін іздеңіз.

if err != nil — және бұл неге ұсқынсыздық емес

a, err := s.Get(slug)
if err != nil {
	return
}

Бұл үш жол Go-да басқаның бәрінен жиі кездеседі, оларды сөгу де әдетке айналған. Мәні бойынша қарсылық айтайық: олар қатенің жолын көрінетін етеді. Ерекше жағдайлары бар тілде қай жол функцияны үзіп кетуі мүмкін екенін кодтан түсіну қиын — мұнда бір қарағаннан түсінікті.

Оның ақысы — сөзуарлық. Пайдасы — қайдан ұшып шыққанына ешқашан таңғалмайсыз.

errors.New және fmt.Errorf

errors.New("мәтін") хабары бар қате жасайды. fmt.Errorf сол істі істейді, бірақ мәндерді қоя алады:

fmt.Errorf("get %q: %w", slug, ErrNotFound)

Мұнда екі етістік бар. %q мақаланың мекенжайын тырнақшамен қояды. %w — ерекшесі: ол басқа қатені жаңасының ішіне орап, оны бүтін күйінде сақтайды.

Сондықтан get "kokek": мақала табылмады деп басылды — алдымен қайда, сосын не.

Елестетіп көріңіз. Конверт ішіндегі конверт. Хат қолынан өткен әркім оны өз конвертіне салып, «пәленшеден алынды» деп жазады. Бәрін ашқанда жолын да, түпнұсқа хатты да көресіз.

Ережесі қарапайым: қате хабары кіші әріппен және нүктесіз жазылады, себебі ол әрдайым дерлік бөтен жолдың ортасында тұрады.

Өз қатеңіз мән ретінде және errors.Is

var ErrNotFound = errors.New("мақала табылмады")

Қатені пакет деңгейіндегі айнымалы етіп жариялайды және Err деген бастамамен атайды. Сонда оны мәтінін талдамай, салыстыруға болады:

if errors.Is(err, ErrNotFound) {

errors.Is конверттердің бәрін ашып, ішінде дәл сол қате бар-жоғын тексереді. Сондықтан мәтіні басқа болып кеткен %w-орамымыз танылды.

Қателерді ешқашан мәтіні бойынша салыстырмаңыз: err.Error() == "мақала табылмады" тұжырымның алғашқы түзетуінен-ақ сынады, әрі үнсіз.

Қатемен не істеу керек

Лайықты үш нұсқа ғана.

  1. Өңдеу. Не істеу керегін білсеңіз — істеңіз: мақаланың орнына 404, әдепкі мән, қайта әрекет.
  2. Орап, жоғары беру. Білмесеңіз — %w-мен өз конвертіңізді қосып, білетін жаққа жіберіңіз.
  3. Журналға жазу. Бір рет және ең жоғарыда, әр қабатта емес.

Және лайықсыз біреуі: жұтып қою. a, _ := s.Get(slug) — заңды Go, компилятор үндемейді, ал бағдарлама бос мақаламен әрі кете береді. Қатенің орнындағы астыңғы сызық оның неге маңызды емесін дауыстап түсіндіре алатын жерде ғана рұқсат етіледі.

panic — қателер үшін емес

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

Табылмаған мақала — ондай жағдай емес. Олар күніне мыңдап болады.

Елестетіп көріңіз. Өрт дабылы. Ол әр ғимаратта бар, солай да дұрыс. Бірақ келуші керек кабинетті таппады екен деп оны ешкім баспайды.

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

Сабақ картасы: функцияның екі шығуы бар — мән және қате

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

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

  1. Тіл тұрғысынан error деген не және интерфейстер сабағынан кейін бұл неге таңғалдырмайды?
  2. fmt.Errorf ішіндегі %w %v-дан несімен өзгеше және оған не байланысты?
  3. err.Error() == "мақала табылмады" қатені білудің неге нашар тәсілі, ал errors.Is — неге жақсы?

Тапсырма

Міндетті. Store типіне Add(slug string, a Article) error әдісін қосыңыз. Тақырып бос болса, ол ErrEmptyTitle қатесін қайтаруы керек, мақала қабылданса — nil. Тест жазыңыз: қалыпты мақала қосып, қате жоқ екеніне көз жеткізіңіз; тақырыбы бос мақала қосып, errors.Is(err, ErrEmptyTitle) тексеріңіз; екіншісінің қоймаға түспегенін тексеріңіз.

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

  • ErrEmptyTitle-ды мақаланың мекенжайын қосып %w арқылы ораңыз да, errors.Is оны бұрынғысынша танитынын тексеріңіз.
  • errors.Unwrap(err) басып шығарып, конверттің ішінде не бар екенін көріңіз.
  • show ішіндегі errors.Is тексеруін мәтіндерді салыстыруға ауыстырыңыз, содан кейін ErrNotFound тұжырымын өзгертіп, не сынатынын қараңыз.

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

Сіз оқып отырған сайт та солай құрылған. Қойма ErrNotFound қайтарады, өңдеуші errors.Is шақырып, 404-пен жауап береді, ал қалғанының бәрі 500-ге және журналдағы бір жолға айналады. Ешқандай ерекше жағдай да, он қабат жоғарыдағы ұстап алу да жоқ — екінші мән мен бір тексеру.

Жауаптар

Жауаптарды көрсету
  1. error — жалғыз Error() string әдісі бар кәдімгі интерфейс. Өткен сабақтан кейін бұл таңғалдырмайды: осы әдісі бар кез келген тип қатеге жарайды, ештеңе жариялаудың қажеті жоқ.
  2. %v қатенің мәтінін қояды, түпнұсқамен байланыс жоғалады. %w қатенің өзін жаңасының ішіне орайды, сондықтан errors.Is пен errors.Unwrap кейін оған жете алады. Шақырушының «табылмады» мен «бәрі бұзылды» дегенді ажырата алуы соған байланысты.
  3. Мәтін — адамға арналған хабар, оны ойланбай өзгертеді: басқа тілге аударды, бір егжей-тегжей қосты — салыстыру жұмыс істемей қалды, әрі үнсіз. errors.Is әріптерді емес, мәндерді салыстырады және тұжырымның кез келген түзетуін көтереді.

Дереккөздер

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

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

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

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

Пікірлер (0)

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