

Golang sucks // Speaker Deck
source link: https://speakerdeck.com/majek04/golang-sucks
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Transcript
-
Golang sucks
Marek Majkowski @majek04 -
Context: Cloudflare
2 -
Don't get me wrong
3 -
Golang is as easy as Python
5 -
Can Golang divide?
• Python: -1 / 2 = ?
• Golang: -1 / 2 = ?
6
-1 -0.5
0 -
Can Golang divide?
• Python: 1 % -2 = ?
• Golang: 1 % -2 = ?
7
-1
1 -
8
func main() {
var a int8
a = -127 / -1;
fmt.Println(a)
}
127 -
9
func main() {
var a int8
a = -128 / -1;
fmt.Println(a)
}
error: constant 128 overflows int8 -
10
func main() {
var a int8
a = -127 - 1;
a = a / -1;
fmt.Println(a)
}
-128 -
11
func main() {
fmt.Println(-11 / 2, -11 >> 1)
}
-5 -6
shift is the same as division? -
12
func main() {
var a uint8
a = 1
a = a << 32
fmt.Println(a)
}
0 in C it's 1 -
Documentation
13 -
Documentation
16 -
Golang is better than C
19 -
Garbage collector
20 -
22
GOGC=11300 -
container/list
24 -
25
(Element)
(Item)
(struct in C) -
26
(Item)
(Element)
⟯
⟯ -
GC + lack of manual memory
management != speed
27 -
Unary negation
28
packet.go:104: the bitwise complement operator is ^ -
&^= operator
29
func main() {
var a uint32 = 0xdeadbabe
a &^= 0xffff0000
fmt.Printf("%08x\n", a)
}
0000babe -
Overflows
30
illegal:
a := uint64(18446744073709551615)+1;
legal:
a := uint64(18446744073709551615);
a = a + 1 -
x == Nan
31
package main
import (
"fmt"
"math"
)
func main() {
a := float32(math.NaN())
fmt.Printf("%f\n", a)
} -
Mandatory downcasting
32
package main
import (
"fmt"
)
func main() {
var a int64
var b int32
b = 1
a = int64(b)
fmt.Println(a)
} -
but casting to interfaces is fine
33 -
34
package main
import (
"fmt"
)
func main() {
for {
switch 1 {
case 1:
default:
}
}
}
Break -
35
package main
import (
"fmt"
)
func main() {
for {
switch 1 {
default:
break // !!!
}
}
fmt.Println("Done!")
} -
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 -
Golang is good for network
programming
37 -
TCPConn.Dial()
38
• bind-before-connect
• SO_REUSEPORT -
40
... impossible to emulate
non-blocking connect()... -
Is FD signed or unsigned?
41 -
Signed vs unsigned
42
• shift >> operator requires unsigned int
• len() returns signed int -
Fd from File is OK
43 -
Fd from Socket is NOT OK
44 -
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)
} -
TCP Backlog
46 -
TCP Backlog
47 -
For network programming access
to BSD API is necessary.
48 -
time.Now went backwards
49 -
Lack of monotonic time
50 -
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
} -
...runes are just int32....
53 -
Good for concurrency
54 -
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)
} -
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 -
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 -
Good libraries
58 -
59
go sort -
go sort is two way
60 -
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;
} -
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 ????
} -
Modern language
63 -
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 { -
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?
} -
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?
}
} -
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))
} -
compare interface
68
(interface{})(0) != (interface{})(uint(0)) -
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 -
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 -
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) -
Assertion vs Conversion
• v = aType(t) // type conversion
• v = t.(aType) // type assertion
72 -
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")
} -
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 -
Golang does not help with
smooth API changes
75 -
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)
} -
interface{}(nil) vs nil
77 -
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()
} -
Errors
79 -
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
} -
Golang doesn't help with
extracting meaning from errors.
81 -
Three indices
82
var array [10]int
slice := array[2:4]
slice = array[2:4:7] -
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 -
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 -
Global namespace NEEDS relative
imports
85 -
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) -
Thanks!
• Solid
• Fast
• Maintainable
88
marek@cloudflare.com @majek04
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK