Golang sucks

D4e1d473a995ef37b3e03e9e6006c3e3?s=47 majek04
November 30, 2017

Golang sucks

I found my notes from learning golang couple of years ago. Here are some things that caught me by surprise.

D4e1d473a995ef37b3e03e9e6006c3e3?s=128

majek04

November 30, 2017
Tweet

Transcript

  1. 4.

    4

  2. 6.

    Can Golang divide? • Python: -1 / 2 = ?

    • Golang: -1 / 2 = ? 6 -1 -0.5 0
  3. 7.

    Can Golang divide? • Python: 1 % -2 = ?

    • Golang: 1 % -2 = ? 7 -1 1
  4. 8.

    8 func main() { var a int8 a = -127

    / -1; fmt.Println(a) } 127
  5. 9.

    9 func main() { var a int8 a = -128

    / -1; fmt.Println(a) } error: constant 128 overflows int8
  6. 10.

    10 func main() { var a int8 a = -127

    - 1; a = a / -1; fmt.Println(a) } -128
  7. 11.

    11 func main() { fmt.Println(-11 / 2, -11 >> 1)

    } -5 -6 shift is the same as division?
  8. 12.

    12 func main() { var a uint8 a = 1

    a = a << 32 fmt.Println(a) } 0 in C it's 1
  9. 14.

    14

  10. 15.

    15

  11. 17.

    17

  12. 18.

    18

  13. 21.

    21

  14. 23.

    23

  15. 29.

    &^= operator 29 func main() { var a uint32 =

    0xdeadbabe a &^= 0xffff0000 fmt.Printf("%08x\n", a) } 0000babe
  16. 31.

    x == Nan 31 package main import ( "fmt" "math"

    ) func main() { a := float32(math.NaN()) fmt.Printf("%f\n", a) }
  17. 32.

    Mandatory downcasting 32 package main import ( "fmt" ) func

    main() { var a int64 var b int32 b = 1 a = int64(b) fmt.Println(a) }
  18. 34.

    34 package main import ( "fmt" ) func main() {

    for { switch 1 { case 1: default: } } } Break
  19. 35.

    35 package main import ( "fmt" ) func main() {

    for { switch 1 { default: break // !!! } } fmt.Println("Done!") }
  20. 36.

    4k on stack? 36 package main func main() { fmt.Println("Start",

    objects()) a := make([]byte, 1 << 18) a[1] = 1 fmt.Println("slice of 1<<18", objects()) b := make([]byte, 1 << 19) b[1] = 1 fmt.Println("sloce of 1<<19", objects()) fmt.Println(">", a[:2], b[:2]) } https://play.golang.org/p/mG01rYDHk8
  21. 39.

    39

  22. 45.

    45 func getTCPSocketFd(socket interface{}) int { // fd is platform-specific

    and not exported socketValue := reflect.ValueOf(socket).Elem() fdValue := socketValue.FieldByName("fd") if !fdValue.IsValid() { panic("socket missing fd on this platform") } netFDValue := fdValue.Elem() sysFDValue := netFDValue.FieldByName("sysfd") if !sysFDValue.IsValid() { panic("socket missing fd on this platform") } return int(sysFD) }
  23. 51.

    51

  24. 52.

    byte slice and string and utf8 • String holds arbitrary

    bytes. It is not required to hold Unicode text. • Range on string is magic: • Fact #3: identifiers are only exported if they’re part of the Unicode uppercase class 52 const nihongo = "⽇日本語" for index, runeValue := range nihongo { fmt.Printf("%#U starts at byte position %d\n", runeValue, i }
  25. 55.

    By value or by reference? 55 func Foo(a T) {

    c := "b" *a = *T(&c) } type T *string func main() { c := "a" a := T(&c) Foo(a) fmt.Printf("%q\n", *a) }
  26. 56.

    By value or by reference? 56 • String --> immutable

    • Pointer to String --> value is immutable • Struct --> mutable, pass-by-value is copying • Pointer to Struct --> mutable • Slice of structs --> mutable • Slice of pointers to structs --> mutable • Maps --> mutable • Chans --> mutable
  27. 57.

    Concurrency is half-baked • map is not thread safe (need

    to use lock) • especially problematic for slow-iterations • Channels are slow • Mutex is still useful • Channels and network don't integrate • But NET package is blocking and has no multiplexing 57
  28. 61.

    C qsort is three-way 61 int compare_ints(const void *p, const

    void *q) { int x = *(const int *)p; int y = *(const int *)q; if (x < y) return -1; else if (x > y) return 1; return 0; }
  29. 62.

    Difference? 62 type T struct { foo Foo bar Bar

    } func Less(a *T, b *T) { if FooLess(a.foo, b.foo) { return BarLess(a.bar, b.bar) } else ???? }
  30. 64.

    64 var fd *os.File var err error if fd, err

    = listenConn.File(); err == nil { var fd *os.File var err error if fd, err := listenConn.File(); err == nil {
  31. 65.

    What is an enum? 65 type Stereotype int const (

    TypicalNoob Stereotype = iota // 0 TypicalHipster // 1 ) func main() { var s Stereotype var i int s = 6 // What type is s? i = int(s) // What type is s? i = int(TypicalNoob) // What type is TypicalNoob? s = TypicalNoob + 1 // What type is TypicalNoob? }
  32. 66.

    66 type Stereotype int const ( TypicalNoob Stereotype = iota

    // 0 TypicalHipster // 1 ) func main() { var s Stereotype s = 6 switch s { case TypicalNoob: fmt.Println("Noob") case TypicalHipster: fmt.Println("Hipster") default: // How come "s" ever get here? } }
  33. 67.

    compare interface 67 func is_blank(value interface{}) bool { switch value.(type)

    { case int: if value == 0 { return true } case uint: if value == 0 { return true } } return false } func main() { var my_val uint = 0 fmt.Println(is_blank(my_val)) }
  34. 69.

    69 func is_blank(value interface{}) bool { switch value.(type) { case

    int: if value == 0 { return true } case uint: if value == uint(0) { // !!!!! return true } } return false } compare interface
  35. 70.

    70 func is_blank(value interface{}) bool { switch value := value.(type)

    { // !!! case int: if value == 0 { return true } case uint: if value == 0 { return true } } return false } compare interface
  36. 71.

    Switching on type 71 package main import ( "fmt" )

    func main() { var i int switch i.(type) { case int: fmt.Println("0") case string: fmt.Println("1") } } > cannot type switch on non-interface value i (type int)
  37. 72.

    Assertion vs Conversion • v = aType(t) // type conversion

    • v = t.(aType) // type assertion 72
  38. 73.

    Casting and interface 73 type Lockable interface { Lock() }

    func main() { var a = Foo() l := a.(Lockable) fmt.Println("lockable") } type Lockable interface { Lock() } func main() { var a = Foo() l := Lockable(a) fmt.Println("lockable") }
  39. 74.

    API evolution • While refactoring my code, can I turn

    a struct into an interface with minimal changes? YES • Can I turn a struct into an interface with zero changes for users of my library? NO • To convert whatever-you-got from API to some interface (Lockable) you need to know if it's a struct, pointer to struct or interface. • It makes sense to cast everything to interface{}. 74
  40. 76.

    Structure inlining 76 type A struct {} type B struct

    {} func (*A)Lock(){ fmt.Println("A") } func (*B)Lock(){ fmt.Println("B") } type C struct { A B } func main() { var c C // c.Lock() // ambiguous selector c.A.Lock() fmt.Println(c) }
  41. 78.

    78 type I interface { F() } type S struct{}

    func (p *S) F() { fmt.Printf("p = %v\n", p) } func main() { // A nil interface value var i I fmt.Printf("i == nil: %t\n\n", i == nil) // A non-nil inteface value, containing a nil value of // underlying type: i = (*S)(nil) fmt.Printf("i == nil: %t\n", i == nil) fmt.Printf("i.(*S) == nil: %t\n", i.(*S) == nil) // We can even call methods of S on it: i.F() }
  42. 79.
  43. 80.

    Errno 80 func ExtractErrno(err error) int { if pe, ok

    := err.(*ProxyError); ok == true { err = pe.Err } if pe, ok := err.(*os.PathError); ok == true { err = pe.Err } if ope, ok := err.(*net.OpError); ok == true { err = ope.Err } if sce, ok := err.(*os.SyscallError); ok == true { err = sce.Err } if se, ok := err.(syscall.Errno); ok == true { return int(se) } return 0 }
  44. 83.

    Vendoring is a mess • Fact #18: if you change

    your github username, you break every package that depended on one of your packages (extra transition milestone!) • golang prefers the use of global namespace for modules which is wrong 83
  45. 84.

    Copy is fine, with relative imports • github.com/cloudflare/golibs • github.com/cloudflare/golibs/foo

    • github.com/cloudflare/golibs/bar • Copy / rename is impossible 84
  46. 86.

    new vs make 86 // Allocate enough memory to store

    a bytes.Buffer value // and return a pointer to the value's address. var buf bytes.Buffer p := &buf // Use a composite literal to perform allocation and // return a pointer to the value's address. p := &bytes.Buffer{} // Use the new function to perform allocation, which will // return a pointer to the value's address. p := new(bytes.Buffer)
  47. 87.

    87