· 8 years ago · Jun 20, 2018, 06:54 PM
1# The Go language for experienced programmers
2
3## Why use Go?
4
5* Like C, but with garbage collection, memory safety, and special mechanisms for concurrency
6* Pointers but no pointer arithmetic
7* No header files
8* Simple, clean syntax
9* Very fast native compilation (about as quick to edit code and restart as a dynamic language)
10* Easy-to-distribute executables
11* No implicit type coercions
12* Simple built-in package system
13* Simple tools
14* Inferred types on variable declarations
15* Slices and maps (feel like arrays and hashmaps in dynamic languages)
16* Explicit error handling with error values and multiple return
17* Interface-based polymorphism
18* Interfaces implicitly implemented (allowing post-hoc interfaces for imported types)
19* Goroutines (runtime managed lightweight threads)
20* Channels for coordinating goroutines and sharing data between them (based on the theory of [CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes))
21
22### Reasons not to use Go
23
24* Immature debugger
25* No generics
26* Garbage collection overhead / pauses (though the pauses are very short)
27* Executable size (the runtime embedded in the output executables is itself ~1MB)
28
29## Base types
30
31```go
32int8 // 8-bit signed int
33int16 // 16-bit signed int
34int32 // 32-bit signed int
35int64 // 64-bit signed int
36
37uint8 // 8-bit unsigned int
38uint16 // 16-bit unsigned int
39uint32 // 32-bit unsigned int
40uint64 // 64-bit unsigned int
41
42float32 // 32-bit float
43float64 // 64-bit float
44
45complex64 // two 32-bit floats
46complex128 // two 64-bit floats
47
48int // 32- or 64-bit signed int (depends upon compilation target)
49uint // 32- or 64-bit unsigned int (depends upon compiliation target)
50
51uintptr // unsigned int large enough to store an address on compilation target
52
53string // a string value is an address referencing UTF-8 data elsewhere in memory
54bool
55
56byte // alias for uint8
57rune // alias for int 32 (used for representing Unicode code points)
58```
59
60## Semi-colon insertion
61
62The semi-colons used in most C-like syntaxes are generally left implicit in Go. Semi-colons are implicit at the end of any line ending with:
63
64* a number, string, or boolean literal
65* an identifier
66* the reserved words *`break continue fallthrough return`*
67* the operators *`++ --`*
68* the end delimiters *`} ] )`*
69
70## Variable declarations and type inference
71
72The full syntax for declaring a variable:
73
74```
75var NAME TYPE; // declare without initialization
76var NAME TYPE = EXPRESSION; // declare with initialization
77```
78
79An uninitialized variable defaults to the 'zero value' for its type. The zero values are:
80
81* numbers: 0
82* strings: ""
83* bools: false
84* pointers: nil
85* structs: (each element is the zero value of its type)
86* arrays: (each element is the zero value of its type)
87* slices: (a null reference with capacity 0 and length 0)
88* interfaces: nil
89
90When a declaration is initialized, we can leave the type inferred from the value's type:
91
92```go
93var foo = "hello" // foo inferred to be a string variable
94var bar = ack() // bar inferred to be whatever type ack() is declared to return
95```
96
97(Keep in mind that all Go operations and functions return a fixed type, so the compiler always knows the type of every expression.)
98
99Go programmers normally use this shorthand for inferred declarations:
100
101```go
102foo := "hello" // declare foo, which is inferred to be a string variable
103bar := ack() // declare bar, which is inferred to be whatever type ack() is declared to return
104```
105
106## Type casting
107
108Unlike many other statically-typed languages, Go is strict about types:
109
110* a variable can only be assigned values of the exact same type
111* each argument to a function must exactly match the corresponding parameter's type
112* the operand types of a binary operation must exactly match
113
114The only place where types need not match exactly is when using interface types (discussed later).
115
116We can cast between certain types:
117
118```go
119var x int = 35
120var y float32 = float32(x) // cast int to float32
121```
122
123Casting between some types preserves the precise value; in other cases, a cast may distort the original value, *e.g.* casts from floats to integers.
124
125## Named types
126
127We can define our own types with a *type* statement. For example:
128
129```go
130type alice int // create a new type 'alice' which is represented as an int
131```
132
133These new types are not aliases: the compiler considers them to be separate, non-interchangeable types. We can however explicitly cast between a named type and the type it is based on (and vice versa) without distoring the value (because the types have the same underlying representation as bits):
134
135```go
136var x int = 3 // an int variable called 'x'
137var y alice // an alice variable called 'y'
138y = 5 // OK! integer literals are considered to have no specific type, and so 5 is a valid alice value
139y = x // compile error! an alice variable cannot be assigned an int value
140 // (even though an alice is really just an int)
141y = alice(x) // OK! cast the int value to an alice value
142x = int(y) // OK! cast the alice value to an int
143```
144
145## Packages
146
147A Go *package* is a namespace and unit of compilation. One source directory constitutes one package, and all of the source files in the directory make up the package.
148
149Source file names must end with `.go`. Certain suffixes starting with underscore can be used to specify that a file should only be compiled for certain platforms, *e.g.* the file `foo_linux.go` will only be included when compiling for Linux. Otherwise, source file names can be anything you want.
150
151The first line of code in each source file must be a package statement declaring the name of the package:
152
153```go
154package foobar // declare that this file is part of the package named foobar
155```
156
157All source files in a directory must declare the same package name.
158
159The package name *main* is special. A package named *main* is compiled into an executable. Any package not named *main* is compiled into an object file.
160
161## Imports
162
163In each source file, you can use by name anything defined in the current package. To use names from another package, that other package must be imported into the current file with an `import` statement:
164
165```go
166package whatever
167// all imports must be at top of the file but after the package statement
168import "otherpackage"
169```
170
171The package to import is specified by its *import path* as a string. An import path tells the Go compiler where to find a package relative to the GOPATH/src directory (GOPATH is an environment variable pointing to your chosen Go working directory):
172
173```go
174import "foo/bar/ack" // import the package in GOPATH/src/foo/bar/ack
175```
176
177A package's import path and its name can be completely different, but by convention the last segment of the import path matches the name, *e.g.* the package at import path *"foo/bar/ack"* would have the name *ack*.
178
179Standard library packages are kept in a subdirectory where the Go tool itself is installed, and they generally have short import paths, like "fmt".
180
181When a package is imported, names of that package are prefixed by the package name and a dot:
182
183```go
184mary.David() // invoke a function David from a package named mary
185```
186
187Only names starting with uppercase letters are *public*, *i.e.* visible to other packages. Names starting with lowercase letters are private to their package.
188
189Packages named *main* cannot be imported by other packages.
190
191Package imports cannot be cyclical, *e.g.* if package A imports package B which imports package C, B cannot import A, and C cannot import either A or B.
192
193## Escape analysis
194
195Unlike C, Go is garbage collected and memory safe. Anything we create in a function call is safe to use even after the call returns. To make this possible, the Go compiler performs *escape analysis*: it determines which things created in a function might be referenced outside the function; only things which the compiler knows for sure will not escape the call in which they are created get stack allocated; everything else is heap allocated.
196
197For example, it is dangerous in a C function to return a pointer to a local variable because the pointed-to-variable no longer exists after the call returns. In Go, however, this is no problem: the compiler detects that the local variable may be used beyond its scope, and so the compiler will allocate the variable on the heap.
198
199## Arrays
200
201Arrays in Go are fixed in size and homogenous:
202
203```go
204var foo [3]int // local variable foo is an array of 3 ints
205var bar [7 + 2]float32 // local variable bar is an array of 9 ints
206foo[0] = 98 // assign 98 to first slot of foo
207bar[8] = -4.21 // assign -4.21 to last slot of bar
208var i int = foo[4] // assign 0 to i (the uninitialized elements of an array start out as zero-values)
209```
210
211The size must be specified by a constant expression. (For dynamically-sized arrays, use *slices*, as discussed later).
212
213Arrays of the same element type but different sizes are considered different types of array.
214
215Arrays of the same type can be assigned to each other and compared with `==` and `!=`:
216
217```go
218var foo [3]int
219var bar [3]int
220foo = bar // copy all values of bar to foo
221foo == bar // true if all respective elements are equal
222```
223
224An array can be created as a literal:
225
226```go
227// an array with 3 ints: 8, 900, -21
228[3]int{8, 900, -21}
229
230// an array with 7 ints (... tells the compiler to infer the size from the number of elements)
231[...]int{1, 2, 3, 4, 5, 6, 7}
232```
233
234These array literals are most commonly passed directly to other functions or assigned to array variables. For example:
235
236```go
237foo([3]byte{9, 100, 30}) // pass an array of 3 bytes to foo()
238
239bar := [5]int{1, 2, 3, 4, 5} // create [5]int variable bar with initial elements 1, 2, 3, 4, 5
240```
241
242We can create arrays of any type, including arrays of arrays. Multi-dimensional arrays have a special literal syntax:
243
244```go
245var foo [3][2]int
246foo = [3][2]int{{1, 2}, {3, 4}, {5, 6}}
247```
248
249## Multiple-return functions
250
251A Go function can return multiple values. The return types are listed in parens:
252
253```go
254// a function with no params returning an int, a byte, and a string
255func foo() (int, byte, string) {
256 return 300, 4, "hi"
257}
258```
259
260A multiple-return function can only be called as a stand-alone statement or as the value of an assignment with the right number of variables:
261
262```go
263foo() // the return values are discarded
264a, b, c := foo() // assign 300 to a, 4 to b, and "hi" to c
265a, b := foo() // compile error! no receiver for the returned string
266bar(foo()) // compile error! cannot call multi-return function in single-value context
267```
268
269If you don't want to use one or more returned values, you can assign them to the special blank identifier (a single underscore):
270
271```go
272a, _, b, _ = ack() // ack returns 4 values, but we only want the first and third
273```
274
275Be careful using `:=` with multiple return: any target variables which don't already exist in the current scope will be implicitly declared:
276
277```go
278var x int
279{
280 x, y := foo() // create a new x in this scope rather than assign to x of the enclosing scope
281}
282```
283
284Avoiding this gotcha requires explicitly declaring the variables with `var`:
285
286```go
287var x int
288{
289 var y int
290 x, y = foo() // assign to x of enclosing scope and y of this scope
291}
292```
293
294## Slices
295
296A slice is a value representing a logical range of indices of an array. For example, given an array of 10 ints, a slice of ints could represent the range from index 3 up to (and including) index 8; this slice would have a length of 5 and a capacity of 6 (because there are 6 elements in the array from index 3 of the array to its end).
297
298Each slice value is made up of three components:
299
300* a reference to a starting element within an array
301* a length (the number of elements represented by the slice)
302* a capacity (length + the remaining number of elements in the array)
303
304A slice's type is determined just by the type of its elements, not by its length or capacity:
305
306```go
307var foo []int // a variable foo that stores int slice values
308var bar []string // a variable bar that stores string slice values
309```
310
311Given an *X* array, we can create an *X* slice with the slice operator. We can get the length and capacity of slices with the built-in `len()` and `cap()` functions:
312
313```go
314foo := [7]int{10, 20, 30, 40, 50, 60, 70}
315var bar []int
316bar = foo[2:5] // a slice representing index 2 up to (but not including) index 5
317
318len(bar) // 3
319cap(bar) // 5
320
321foo[2] // 30
322bar[0] // 30
323
324foo[3] // 40
325bar[1] // 40
326
327foo[4] // 50
328bar[2] // 50
329
330foo[5] // 60
331bar[3] // panic! exceeded bounds of the slice
332
333// create a slice from a slice
334ack := bar[1:3]
335len(ack) // 2
336cap(ack) // 4
337ack[0] // 40
338
339// the ranges represented by ack and bar overlap in memory
340ack[0] = 41
341bar[1] // 41
342bar[1] = 42
343ack[0] // 42
344// changes through the slice modify the underlying array
345foo[3] // 42
346
347// assign the slice value (reference, length, capacity) of ack to the variable bar
348bar = ack
349```
350
351In the slice operator, the number before the colon defaults to 0, and the number after the colon defaults to the length of the array or slice. Effectively, `foo[:]` returns a slice representing the whole range of `foo`.
352
353The special built-in function `make()` returns a slice with a new underlying array:
354
355```go
356make([]int, 7, 10) // create an int array of size 10 and return a slice of indexes 0:7 of this new array
357```
358
359(Notice that `make()` takes a type as its first argument, so it is clearly not an ordinary function.)
360
361## Variadic functions
362
363If we want a function to take a varying number of inputs, we could simply make a parameter a slice, to which callers would then pass a slice with 0 or more elements:
364
365```go
366func foo(a string, b []int) { /* do stuff */ }
367
368foo("hi", []int{6, 2, -11})
369foo("yo", []int{5})
370foo("bye", []int{})
371```
372
373This certainly works, but the `[]type{}` syntax makes these calls a little cluttered. To clean up this pattern, the last parameter of a Go function can be a slice denoted by `...` (elipses) instead of `[]`; the slice passed to this parameter is assembled from 0 or more values *not* enclosed in the `[]type{}` syntax:
374
375```go
376func foo(a string, b ...int) { /* do stuff */ }
377
378foo("hi", 6, 2, -11) // []int{6, 2, -11} is passed to b
379foo("yo", 5) // []int{5} is passed to b
380foo("bye") // []int{} is passed to b
381
382// explicitly pass an actual slice by suffixing ...
383nums := []int{8, 3, 4}
384foo("aloha", nums...) // nums is passed to b
385```
386
387## Appending to slices
388
389To append ints to an int slice no matter its capacity, we can define an append function:
390
391```go
392func append(sl []int, vals ...int) []int {
393 newLen := len(sl) + len(vals)
394 if cap(sl) >= newLen {
395 // original underlying array was large enough for appending the new values
396 copy(vals, sl[len(sl):cap(sl)])
397 return sl[:newLen] // same slice range, but extended
398 } else {
399 // original underlying array was not large enough
400 newSl := make([]int, newLen) // create new, larger underlying array
401 copy(sl, newSl) // copy existing values
402 copy(vals, newSl[len(sl):]) // append the new values
403 return newSl
404 }
405}
406```
407
408Go has no mechanism for defining generic functions, but a few generic functions are provided as built-ins. The built-in `append()` is generic: it takes slices of any type and returns the slice type passed to it, `e.g.` passing a slice of bytes to `append()` returns a slice of bytes.
409
410## Maps
411
412A map is a hashmap of key-value pairs. The values can be any type; the keys cannot be functions, slices, or maps.
413
414A map variable is just a reference. To create an actual map, use `make()`:
415
416```go
417var foo map[string]int
418foo = make(map[string]int) // create a new empty map
419foo["hello"] = 10 // assign value 10 to key "hello"
420```
421
422We can create a map with literal syntax:
423
424```go
425foo := map[string]int{"hi": 9, "bye": 11}
426```
427
428The built-in `delete()` removes a key from a map:
429
430```go
431foo := map[string]int{"hi": 9, "bye": 11}
432delete(foo, "hi") // remove the key "hi"
433```
434
435## For-range loops
436
437A `for-range` loop is Go's equivalent of *for-in*/*foreach* in other languages:
438
439```go
440for i := range arr {
441 // i iterates from 0 up to (but not including) len(arr)
442}
443
444// when range is assigned to two variables, the first is the index, the second is the value
445for i, v := range arr {
446 // i is the index, v is the value
447}
448```
449
450We can use `for-range` to iterate through the elements of a map:
451
452```go
453// the iteration order is random
454for k := range m {
455 // k is a key from m
456}
457
458for k, v := range m {
459 // k is a key from m and v is its corresponding value
460}
461```
462
463## Structs
464
465A *struct* (short for *structure*) is a programmer-defined data type made up of other types:
466
467```go
468type Cat struct {
469 Lives int
470 Age float32
471 Weight float32
472 Name string
473}
474
475var c Cat // fields start out as 'zero' values
476c.Name = "Mittens"
477c.Lives = 9
478c.Age = 4.3
479```
480
481Structs cannot be directly recursive:
482
483```go
484type Cat struct {
485 Lives int
486 Age float32
487 Weight float32
488 Name string
489 Mother Cat // compile error! every Cat would contain an infinite number of other Cats
490}
491```
492
493A struct can however be indirectly recursive through some kind of reference:
494
495```go
496type Cat struct {
497 Lives int
498 Age float32
499 Weight float32
500 Name string
501 Mother *Cat // a pointer to Cat is OK!
502}
503```
504
505## Pointers
506
507Go pointers are basically just like C pointers except Go has no pointer arithmetic. A pointer represents a typed address, e.g. an address of an int, an address of a string, an address of a byte, *etc.* We can get a pointer to a variable using `&` (the reference operator); using & on a variable of type *X* returns an *X pointer*.
508
509```go
510var i int
511var b byte
512var ip *int // variable ip stores int pointers
513var bp *byte // variable bp stores byte pointers
514ip = &i // assign ip the address of i
515bp = &b // assign bp the address of b
516ip = &b // compile error! &b returns a byte pointer, not an int pointer
517```
518
519We can also use & to get a pointer to a struct field:
520
521```go
522var c Cat
523var fp *float32
524fp = &c.Age
525```
526
527We can get the value pointed to by a pointer using * (the dereference operator), and we can modify the storage pointed to by a pointer using * on the target of assignment:
528
529```go
530var f float32 = 8.2
531var fp *float32 = &f
532*fp // 8.2
533*fp = 14.7 // assign 14.7 to storage pointed to by fp
534f // 14.2
535```
536
537The zero-value of a pointer is `nil`. Dereferencing a nil pointer triggers a panic.
538
539We can create pointers of any type, even pointers to pointers. However, multi-degree pointers tend to be used much less in Go code than in C code, largely because slices and interfaces fill many of the same use cases as pointers.
540
541## Anonymous functions
542
543A function variable stores the address of a function:
544
545```go
546function bar (a int, b string) (float32, []int) { /* body */ }
547function ack (a int) { /* body */ }
548
549var foo func (int, string) (float32, []int)
550foo = bar // OK
551foo = ack // compile error! wrong type of function
552```
553
554We can create nested functions using expression syntax:
555
556```go
557foo := func (a int) int {
558 return a * 5
559}
560
561// OK if bar takes a function taking a string and returning nothing
562bar(func (a string) {
563 fmt.Println(a)
564})
565```
566
567Just like in Javascript, a nested function in Go is a closure over its containing function(s), meaning that any variables it uses from the enclosing call(s) persist, even after the enclosing function(s) return:
568
569```go
570func foo() {
571 a := 3 // 'a' is used by the nested function and so persists even after the call to foo() returns
572 return func () {
573 a += 2
574 fmt.Println()
575 }
576}
577
578f := foo()
579f() // 5
580f() // 7
581f() // 9
582```
583
584## Methods
585
586A Go method is like a function but with a special *receiver* parameter. Only named types and pointers to named types can be method receivers. Each of these types has its own table of methods (and effectively its own namespace of methods).
587
588```go
589type alice int
590
591// 'a' is the receiver
592func (a alice) foo() int {
593 return int(a) + 3
594}
595
596a := alice(5)
597a.foo() // 8
598```
599
600If a named type has a method *foo*, the pointer to that named type cannot have its own method *foo*, and *vice versa*: either type *X* has a method *foo*, or \**X* has a method *foo*, but never both.
601
602We can invoke methods of \*X on instances of X, in which case the reference with & is implicit:
603
604```go
605func (x *X) foo() { /* body */ }
606
607var bar X
608bar.foo() // (&bar).foo()
609```
610
611Likewise, we can invoke methods of X on pointers to X, in which case the dereference with * is implicit:
612
613```go
614func (x X) foo() { /* body */ }
615
616var bar *X
617bar.foo() // (*bar).foo()
618```
619
620## Interfaces
621
622Go interfaces are very much like Java and C# interfaces: an interface is a type defined by a set of method signatures rather than any concrete code or data.
623
624Unlike in Java and C#, which types implement which interfaces is not stated by the programmer explicitly: if a type has methods matching all the signatures listed in an interface, that type implicitly implements the interface.
625
626If an interface method is implemented on X, then \*X is considered to implement the interface method as well. Effectively, \*X is considered to implement an interface if the methods are all implemented on X, or on \*X, or some mix thereof. However, X is considered to implement an interface only if the methods are *all* implemented on X itself.
627
628An interface value is made up of two references:
629
630* A reference to a value of a type implementing the interface
631* A reference to the type of the value itself (the type is represented in memory as the type's table of methods)
632
633
634All types which implement an interface are considered valid subtypes of the interface. So given two types Cat and Dog which both implement interface Animal, we can assign Dog and Cat values to an Animal variable:
635
636```go
637var c Cat
638var d Dog
639var a Animal
640a = c // OK!
641a = d // OK!
642```
643
644No matter what type of value we assign to the Animal variable, we can only use that variable as an Animal, not as a Dog or Cat:
645
646```go
647var d Dog
648var a Animal = d
649a.sleep() // OK (assuming sleep is a method of Animal)
650a.bark() // compile error! (assuming bark is not a method of Animal)
651var f float32 = a.age // compile error! (interfaces do not have fields)
652```
653
654We can branch over the possible types of an interface value with a *type switch*:
655
656```go
657var x Animal;
658// ... assume x is assigned a concrete type of Animal
659switch v := x.(type) {
660case Cat: // if x references a Cat
661 // v is of type Cat
662case Dog: // if x references a Dog
663 // v is of type Dog
664default:
665 // v is of type Animal
666}
667```
668
669## Goroutines
670
671A goroutine is a thread of execution managed by the Go runtime rather than directly by the OS. The Go runtime manages a pool of OS threads and schedules the goroutines to run in the OS threads.
672
673Creating a program with many OS threads is generally impractical because each OS thread incurs significant overhead; creating a program with many thousands of goroutines is generally practical because goroutines are relatively cheap.
674
675A `go` statement spawns a new goroutine:
676
677```go
678go foo() // kick off new thread that starts with a call to foo()
679bar() // executed in the current thread (so does not wait for foo() to return)
680```
681
682## Channels
683
684
685## Select statements
686
687## Panic and recover
688
689Most programming languages introduced in the last few decades are designed around exceptions for error handling. Go does have a mechanism nearly like exceptions called 'panics', but panics are reserved for backing out of code in the event of out-right bugs, such as exceeding array bounds. It's generally improper to catch panics (or *recover* them, as we say in Go), except as last-ditch opportunities for our programs to fail gracefully.
690
691## Error interface
692
693For eventualities like failures to read files or open network connections, Go favors explicitly returned error values rather than panics. Very simply, the caller of a function must check the return value for error conditions. Thanks to multiple return, this is not as clunky as it is in C.
694
695Go also makes error values easier to handle by defining an *Error* interface. By convention, all error values in Go implement the *Error* interface, and the declared return type for an error value is (almost) always *Error*. Thus, a function can return different types of error values from different branches. The caller is expected to know which kind of error values the function might return and branch accordingly.