Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ловушки Go, в которые часто попадают новички

Ловушки Go, в которые часто попадают новички

Iskander (Alex) Sharipov

December 08, 2018
Tweet

More Decks by Iskander (Alex) Sharipov

Other Decks in Programming

Transcript

  1. Ловушки Go, в которые часто
    попадают новички
    Колистратова Дарья
    Software Engineer, RetailNext

    View Slide

  2. Фокус #1
    VS
    package main
    type T struct{}
    func (t T) foo() { println("foo") }
    func main() {
    new(T).foo()
    T{}.foo()
    } Run
    package main
    type T struct{}
    func (t *T) foo() { println("foo") }
    func main() {
    new(T).foo()
    T{}.foo()
    } Run
    2

    View Slide

  3. Разоблачение фокуса #1 - ресиверы методов
    Докуменация Go:
    "A method call x.m() is valid if the method set of (the type of) x contains m and the argument
    list can be assigned to the parameter list of m. If x is addressable and &x’s method set
    contains m, x.m() is shorthand for (&x).m()"
    3

    View Slide

  4. Модификация фокуса #1
    package main
    import "fmt"
    type T struct{}
    func (t T) foo() { fmt.Printf("foo: received type %T, ", t) }
    func (t *T) bar() { fmt.Printf("bar: received type %T, ", t) }
    func main() {
    t := T{}
    pt := &t
    pt.foo()
    fmt.Printf("passed type %T\n", pt)
    t.foo()
    fmt.Printf("passed type %T\n", t)
    pt.bar()
    fmt.Printf("passed type %T\n", pt)
    t.bar()
    fmt.Printf("passed type %T\n", t)
    } Run
    4

    View Slide

  5. Разоблачение модификации фокуса #1 - ресиверы методов
    Докуменация Go:
    "As with selectors, a reference to a non-interface method with a value receiver using a
    pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
    As with method calls, a reference to a non-interface method with a pointer receiver using an
    addressable value will automatically take the address of that value: t.Mp is equivalent to
    (&t).Mp."
    5

    View Slide

  6. Фокус #2
    package main
    import "fmt"
    func main() {
    ints := []int{1, 2, 3}
    intsReferences := make([]*int, len(ints))
    for i, item := range ints {
    intsReferences[i] = &item
    }
    for _, item := range intsReferences {
    fmt.Printf("%d ", *item)
    }
    } Run
    6

    View Slide

  7. Разоблачение фокуса #2 - взятие адреса переменной цикла
    Записывали адрес переменной item, которая в конце итерации хранит значение
    последнего элемента
    Надо: записывать адрес реального значения массива
    package main
    import "fmt"
    func main() {
    ints := []int{1, 2, 3}
    intsReferences := make([]*int, len(ints))
    for i, _ := range ints {
    intsReferences[i] = &ints[i]
    }
    for _, item := range intsReferences {
    fmt.Printf("%d ", *item)
    }
    } Run
    7

    View Slide

  8. Фокус #3
    VS
    package main
    import "fmt"
    func main() {
    for i := 0; i < 3; i++ {
    defer func() {
    fmt.Printf("%d ", i)
    }()
    }
    } Run
    package main
    import "fmt"
    func main() {
    for i := 0; i < 3; i++ {
    defer fmt.Printf("%d ", i)
    }
    } Run
    8

    View Slide

  9. Разоблачение фокуса #3 - порядок вычисления аргументов defer
    defer в цикле - опасный трюк, просьба не повторять в домашних условях!*
    Докуменация Go:
    "Each time a "…defer" statement executes, the function value and parameters to the call are
    evaluated as usual and saved anew but the actual function is not invoked…"
    *но если очень хочется, то можно
    9

    View Slide

  10. Фокус #4
    package main
    import "fmt"
    type Person interface{ Name() string }
    type User struct{ name string }
    func (u *User) Name() string { return u.name }
    func getUserFromDB() *User {
    return nil // No such user in db so return nil
    }
    func getPerson() Person {
    return getUserFromDB()
    }
    func main() {
    if p := getPerson(); p != nil {
    fmt.Printf("The person name is %s\n", p.Name())
    }
    } Run
    10

    View Slide

  11. Разоблачение фокуса #4 - проверка интерфейса на nil
    Интерфейс nil, если и значение, и его тип nil
    Надо: возвращать nil
    func getPerson() Person {
    if u := getUserFromDB(); u != nil {
    return u
    }
    return nil
    }
    11

    View Slide

  12. Что дальше?
    Слушать компилятор
    Пользоваться линтерами
    Читать документацию го:
    golang.org/doc/ (https://golang.org/doc/)
    Читать интересные статьи, например:
    "Gotchas of Defer in Go" (https://blog.learngoprogramming.com/gotchas-of-defer-in-go-1-8d070894cb01)
    "50 Shades of Go" (http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#slice_data_corruption)
    Быть внимательными! Ловушки бывают хитро замаскированы.
    12

    View Slide

  13. Внимание, конкурс!
    Первый приз уходит тому, кто быстрее всех верно решил 4 задания, а оставшийся
    приз уходит счастливчику, который решил правильно 3 задания и оказался
    выбранным генератором случайных чисел.
    Cсылка на задания доступна в телеграм-канале
    и группе вконтакте:
    https://t.me/golang_events_nizhny (https://t.me/golang_events_nizhny)
    https://vk.com/golang_nizhny ( https://vk.com/golang_nizhny) 13

    View Slide

  14. Вопросы?
    14

    View Slide

  15. Thank you
    Колистратова Дарья
    Software Engineer, RetailNext
    da[email protected] (mailto:[email protected])
    https://www.facebook.com/daria.kolistratova (https://www.facebook.com/daria.kolistratova)
    https://vk.com/dakolistratova (https://vk.com/dakolistratova)

    View Slide

  16. View Slide