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. Фокус #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
  2. Разоблачение фокуса #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
  3. Модификация фокуса #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
  4. Разоблачение модификации фокуса #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
  5. Фокус #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
  6. Разоблачение фокуса #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
  7. Фокус #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
  8. Разоблачение фокуса #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
  9. Фокус #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
  10. Разоблачение фокуса #4 - проверка интерфейса на nil Интерфейс nil,

    если и значение, и его тип nil Надо: возвращать nil func getPerson() Person { if u := getUserFromDB(); u != nil { return u } return nil } 11
  11. Что дальше? Слушать компилятор Пользоваться линтерами Читать документацию го: 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
  12. Внимание, конкурс! Первый приз уходит тому, кто быстрее всех верно

    решил 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