Най-добри практики - Работа с грешки

Това е първата статия от поредица от уроци, които научих през няколко години, в които съм работил с Go в производство. Ние предлагаме голям брой Go услуги в производството на Saltside Technologies (psst, наемам за няколко позиции в Бангалор за Saltside) и също така ръководя собствен бизнес, където Go е неразделна част.

Ще обхванем широк кръг от теми, големи и малки.

Първата тема, която исках да разгледам в тази серия, е работа с грешки. Често предизвиква объркване и раздразнение за новите разработчици на Go.

Някакъв фон - Интерфейсът за грешка

Просто сме на една и съща страница. Както може би знаете грешка в Go е просто всичко, което реализира интерфейса за грешка. Ето как изглежда определението на интерфейса:

тип грешка на интерфейса {
    Грешка () низ
}

Така че всичко, което реализира метода на низ Error (), може да се използва като грешка.

Проверка за грешки

Използване на структури за грешки и проверка на типа

Когато започнах да пиша Go, често правех стриктни сравнения на съобщения за грешки, за да видя какъв е видът на грешката (да, неудобно е да се мисли, но понякога трябва да погледнете назад, за да продължите напред).

По-добър подход е да се използват типове грешки. Така че можете (разбира се) да създавате структури, които реализират интерфейса за грешка и след това да правите сравнение в оператор за превключване.

Ето пример за внедряване на грешки.

тип ErrZeroDivision структура {
    низ за съобщение
}
func NewErrZeroDivision (низ на съобщение) * ErrZeroDivision {
    връщане & ErrZeroDivision {
        съобщение: съобщение,
    }
}
func (e * ErrZeroDivision) Грешка () низ {
    върнете е.месеца
}

Сега тази грешка може да се използва така.

func main () {
    резултат, грешка: = разделяне (1.0, 0.0)
    ако греши! = нула {
        грешка при превключване (тип) {
        случай * ErrZeroDivision:
            fmt.Println (err.Error ())
        по подразбиране:
            fmt.Println ("Какво се случи току-що?")
        }
    }
    fmt.Println (резултат)
}
func раздели (a, b float64) (float64, грешка) {
    ако b == 0,0 {
        връщане 0.0, NewErrZeroDivision ("Не мога да се раздели на нула")
    }
    връщане a / b, нула
}

Ето връзката Go Play за пълния пример. Забележете шаблона на грешка (тип) на превключвателя, който дава възможност за проверка за различни типове грешки, а не за нещо друго (като сравняване на низове или нещо подобно).

Използване на пакета за грешки и директно сравнение

Горепосоченият подход може да се управлява алтернативно чрез пакета за грешки. Този подход е препоръчителен за проверка на грешки в пакета, където се нуждаете от бързо представяне на грешки.

var errNotFound = error.New ("Елементът не е намерен")
func main () {
    err: = getItem (123) // Това би хвърли errNotFound
    ако греши! = нула {
        грешка при превключване {
        случай errNotFound:
            log.Println ("Исканият елемент не е намерен")
        по подразбиране:
            log.Println ("Неизвестна грешка възникна")
        }
    }
}

Този подход е по-малко добър, когато се нуждаете от по-сложни обекти за грешка с напр. кодове за грешки и т.н. В този случай трябва да създадете свой собствен тип, който реализира интерфейса за грешки.

Незабавно боравене с грешки

Понякога се натъквам на код като по-долу (но обикновено с повече пух ..):

func example1 () грешка {
    грешка: = call1 ()
    грешка при връщане
}

Въпросът тук е, че грешката не се обработва веднага. Това е крехък подход, тъй като някой може да вмъкне код между err: = call1 () и return err, което би нарушило намерението, тъй като това може да засенчи първата грешка. Два алтернативни подхода:

// Свиване на връщането и грешката.
func example2 () грешка {
    връщане call1 ()
}
// Извършете изрична работа с грешки веднага след разговора.
func example3 () грешка {
    грешка: = call1 ()
    ако греши! = нула {
        грешка при връщане
    }
    връщане нула
}

И двата по-горе подхода са добре с мен. Те постигат едно и също нещо, което е; ако някой трябва да добави нещо след call1 (), той трябва да се погрижи за обработката на грешки.

Това е всичко за днес

Следете следващата статия за Go Best Practices. Върви силен :).

func main () {
    грешка: = readArticle ("Най-добри практики - работа с грешки")
    ако греши! = нула {
        пинг ( "@ sebdah")
    }
}