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

Golang sucks

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.

majek04

November 30, 2017
Tweet

More Decks by majek04

Other Decks in Programming

Transcript

  1. Golang sucks
    Marek Majkowski @majek04

    View Slide

  2. Context: Cloudflare
    2

    View Slide

  3. Don't get me wrong
    3

    View Slide

  4. 4

    View Slide

  5. Golang is as easy as Python
    5

    View Slide

  6. Can Golang divide?
    • Python: -1 / 2 = ?
    • Golang: -1 / 2 = ?
    6
    -1 -0.5
    0

    View Slide

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

    View Slide

  8. 8
    func main() {
    var a int8
    a = -127 / -1;
    fmt.Println(a)
    }
    127

    View Slide

  9. 9
    func main() {
    var a int8
    a = -128 / -1;
    fmt.Println(a)
    }
    error: constant 128 overflows int8

    View Slide

  10. 10
    func main() {
    var a int8
    a = -127 - 1;
    a = a / -1;
    fmt.Println(a)
    }
    -128

    View Slide

  11. 11
    func main() {
    fmt.Println(-11 / 2, -11 >> 1)
    }
    -5 -6
    shift is the same as division?

    View Slide

  12. 12
    func main() {
    var a uint8
    a = 1
    a = a << 32
    fmt.Println(a)
    }
    0 in C it's 1

    View Slide

  13. Documentation
    13

    View Slide

  14. 14

    View Slide

  15. 15

    View Slide

  16. Documentation
    16

    View Slide

  17. 17

    View Slide

  18. 18

    View Slide

  19. Golang is better than C
    19

    View Slide

  20. Garbage collector
    20

    View Slide

  21. 21

    View Slide

  22. 22
    GOGC=11300

    View Slide

  23. 23

    View Slide

  24. container/list
    24

    View Slide

  25. 25
    (Element)
    (Item)
    (struct in C)

    View Slide

  26. 26
    (Item)
    (Element)


    View Slide

  27. GC + lack of manual memory
    management != speed
    27

    View Slide

  28. Unary negation
    28
    packet.go:104: the bitwise complement operator is ^

    View Slide

  29. &^= operator
    29
    func main() {
    var a uint32 = 0xdeadbabe
    a &^= 0xffff0000
    fmt.Printf("%08x\n", a)
    }
    0000babe

    View Slide

  30. Overflows
    30
    illegal:
    a := uint64(18446744073709551615)+1;
    legal:
    a := uint64(18446744073709551615);
    a = a + 1

    View Slide

  31. x == Nan
    31
    package main
    import (
    "fmt"
    "math"
    )
    func main() {
    a := float32(math.NaN())
    fmt.Printf("%f\n", a)
    }

    View Slide

  32. Mandatory downcasting
    32
    package main
    import (
    "fmt"
    )
    func main() {
    var a int64
    var b int32
    b = 1
    a = int64(b)
    fmt.Println(a)
    }

    View Slide

  33. but casting to interfaces is fine
    33

    View Slide

  34. 34
    package main
    import (
    "fmt"
    )
    func main() {
    for {
    switch 1 {
    case 1:
    default:
    }
    }
    }
    Break

    View Slide

  35. 35
    package main
    import (
    "fmt"
    )
    func main() {
    for {
    switch 1 {
    default:
    break // !!!
    }
    }
    fmt.Println("Done!")
    }

    View Slide

  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

    View Slide

  37. Golang is good for network
    programming
    37

    View Slide

  38. TCPConn.Dial()
    38
    • bind-before-connect
    • SO_REUSEPORT

    View Slide

  39. 39

    View Slide

  40. 40
    ... impossible to emulate
    non-blocking connect()...

    View Slide

  41. Is FD signed or unsigned?
    41

    View Slide

  42. Signed vs unsigned
    42
    • shift >> operator requires unsigned int
    • len() returns signed int

    View Slide

  43. Fd from File is OK
    43

    View Slide

  44. Fd from Socket is NOT OK
    44

    View Slide

  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)
    }

    View Slide

  46. TCP Backlog
    46

    View Slide

  47. TCP Backlog
    47

    View Slide

  48. For network programming access
    to BSD API is necessary.
    48

    View Slide

  49. time.Now went backwards
    49

    View Slide

  50. Lack of monotonic time
    50

    View Slide

  51. 51

    View Slide

  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
    }

    View Slide

  53. ...runes are just int32....
    53

    View Slide

  54. Good for concurrency
    54

    View Slide

  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)
    }

    View Slide

  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

    View Slide

  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

    View Slide

  58. Good libraries
    58

    View Slide

  59. 59
    go sort

    View Slide

  60. go sort is two way
    60

    View Slide

  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;
    }

    View Slide

  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 ????
    }

    View Slide

  63. Modern language
    63

    View Slide

  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 {

    View Slide

  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?
    }

    View Slide

  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?
    }
    }

    View Slide

  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))
    }

    View Slide

  68. compare interface
    68
    (interface{})(0) != (interface{})(uint(0))

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

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

    View Slide

  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")
    }

    View Slide

  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

    View Slide

  75. Golang does not help with
    smooth API changes
    75

    View Slide

  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)
    }

    View Slide

  77. interface{}(nil) vs nil
    77

    View Slide

  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()
    }

    View Slide

  79. Errors
    79

    View Slide

  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
    }

    View Slide

  81. Golang doesn't help with
    extracting meaning from errors.
    81

    View Slide

  82. Three indices
    82
    var array [10]int
    slice := array[2:4]
    slice = array[2:4:7]

    View Slide

  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

    View Slide

  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

    View Slide

  85. Global namespace NEEDS relative
    imports
    85

    View Slide

  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)

    View Slide

  87. 87

    View Slide

  88. Thanks!
    • Solid
    • Fast
    • Maintainable
    88
    [email protected]flare.com @majek04

    View Slide