Go Notes
Basics
-
Hello World:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
package main import ( "fmt" "strings" "math/rand" "time" "github.com/headfirstgo/keyboard" // use go get to download the package ) func main() { fmt.Println("Hello Go!") fmt.Println(strings.Title("head first go")) rand.Seed(time.Now().Unix()) fmt.Println(rand.Intn(100)) // 0 - 99 }
-
Use
go fmt
to format the code. (Go uses tabs instead of spaces.) -
Use
go run
to compile and run. Orgo build
and./foo
.go install
generates the binary in the bin directory. -
Rune literals are surrounded by single quotes. (Just like C chars.)
-
Initialization:
1 2 3 4 5
var foobar bool // the zero (default) value is false // The zero values: 0 for int and float; false for bool foo := 42 // short variable declaration // ONLY ONE variable in the short variable declaration has to be new // This allows multiple err for definitions
-
Go prefers the camelCase and no semicolons.
-
Type conversions:
- Go uses the C-like style type conversion:
length = float64(width)
- And similarly for string to int:
foo, err := strconv.Atoi("123")
(ASCII to Int)
- Go uses the C-like style type conversion:
-
Go doesn’t allow variable declarations unless we use these variables.
-
We can use the blank identifier just like in Swift:
1 2 3 4 5 6
reader := bufio.NewReader(os.Stdin) input, _ := reader.ReadString('\n') // if we don't handle the err // or we handle it: // if err != nil { // log.Fatal(err) //}
-
Like in Swift/Python, there’s no need to add parentheses for
if
andfor
. -
Since Go is syntactically similar to C, it also uses the formatting verbs:
%f
,%d
,%s
,%t
(Boolean),%v
(Any.) (%2d
and%.2f
are also used to round the numbers.) -
Like in Swift and Kotlin, the types are after the parameter names in a function:
1 2 3 4 5 6 7
func foo(bar string, foobar int) string { return "Hello Go!" } func foofoo() (string, string) { return "Hello", "Go!" }
-
Go is a “pass-by-value” language by default.
-
And we have the C-style pointers and addresses. A shorter form:
fooPointer := &foo
*fooPointer = 42
fmt.Println(*fooPointer)
. -
Use pointers to change the value for functions:
1 2 3 4 5
func myFunc(foo *bar) { foo.foofoo ++ } myFunc(foofoofoo&)
-
-
const fooBar = 42
orconst fooBar int = 42
-
Documentations:
go doc strconv
to see the documentation of strconv.go doc strconv Atoi
to see the specific function.godoc -http=:6060
starts the local server at port 6060.
-
Add ordinary comments before the package line and before functions to make them doc comments. A few conventions:
- Comments should be complete sentences.
- Package comments should begin with “Package + name”:
// Package myPackage does nothing.
. - Function comments should begin with the function name:
// MyFunction does nothing.
.
-
import ("os")
andos.Args[1:]
gives the command-line arguments (except the file name). -
Variadic functions:
func myFunc (param1 int, param2 ...string) {}
(unlimited string parameters stored as a alice in param2) -
Functions are first-class in go as well.
1 2 3 4
func twice(theFunction func()) { theFunction() theFunction() }
Data Structures
-
Arrays:
var myArray [4]string
myArray := [2]int{3, 4}
fmt.Println(myArray)
(can be printed directly).- Like in Python, use
len(meArray)
to check the length.
-
For loops:
for index, value := range myArray
1 2 3 4 5 6 7 8 9 10 11
for index, note := range notes { fmt.Println(index, note) } // or for _, note := range notes { fmt.Println(note) } // or for index, _ := range notes { fmt.Println(index) }
-
Slices: (similar to Python)
var notes []string
thennotes = make([]string, 7)
- Or
notes := []string{"foo", "bar"}
myArray := [5]string{"a", "b, "c", "d", "e"}
mySlice := myArray[1:3]
gives b, c.- We can also do
[1:]
or[:3]
. - The modifications for the original array or the slice will affect each other.
slice = append(slice, "foo", "bar", "foobar")
-
Maps:
-
var myMap map[string]int
thenmyMap = make(map[string]int)
. -
Or
myMap := make(map[string]int)
. -
myMap["newKey"] = 1
to insert andmyMap["newKey"]
to access. -
Map literals:
1 2 3 4 5 6 7
ranks := map[string]int{"bronze": 3, "silver": 2, "gold": 1} // or multiline elements := map[string]string{ "H": "Hydrogen", "Li": "Lithium", } emptyMap := map[string]int{} // empty
-
Use
ok
(a boolean value) to tell if it’s a zero value:grade, ok := grades[name]
thenif !go {}
. -
Use
delete(myMap, "myKey")
to delete the pair. -
for loops:
for key, value := range myMap {}
for key := range myMap {}
for _, value := myMap {}
-
sort.Strings(names)
and then loop to have the sorted map.
-
Structs
-
Example: (
go fmt
will format the spaces)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// Capitalize if we want to export the package // Lowercase if we want to make it private in its own package var MyStruct struct { Number float64 Word string Toggle bool } MyStruct.number = 3.14 // or define a type type Car struct { Name string TopSpeed float64 } var myCar Car // or struct literal myCar := Car{Name: "Tesla", TopSpeed: 337} // Anonymous fields with just the type name is also allowed func (m *Car) myMethod() { fmt.Println("This is a method from Car %s", m) *m.TopSpeed = 1 } func (m *Car) SetTopSpeed(speed float64) error { // must pass by reference if speed < 0 { return errors.New("Invalid speed") } m.TopSpeed = speed return nil } func (m *Car) TopSpeed() float64 { // Don't add Get to the name in the Getter return m.TopSpeed }
-
Interfaces:
-
Example:
1 2 3 4 5 6 7 8 9 10 11 12
type MyInterface interface { Foo() Bar(float64) FooBar() string } func (m MyType) Foo() {} func (m MyType) Bar(f float64) {} func (m MyType) FooBar() string { return "Now I'm complete" } // No further stuff needed. MyType automatically matches the interface now, // if it has defined all the methods.
-
Type assertion:
robot, ok := noiseMaker.(Robot)
(noiseMaker is the interface var). -
The empty interface can accept any type.
func AcceptAnything(thing interface{})
-
Errors and Failures
-
Add
defer
to make sure a function call takes place, even if the calling function exist early (returned or panicking).defer fmt.Println("I'm deferred to the end.")
. -
panic("I'm panicking")
to create a panic (crashing the program). -
recover
takes the return value ofpanic
and exits the program normally.1 2 3 4 5 6 7 8 9 10 11
func calmDown() { recover() } func freakOut() { defer calmDown() panic("Oh no") fmt.Println("I won't be run!") } func main() { freakout() }
1 2 3 4 5 6 7 8 9 10 11 12
func calmDown() { p := recover() err, ok := p.(error) // assert that it's an error if ok { fmt.Println(err.Error()) } } func main() { defer calmDown() err := fmt.Errorf("There's an error") panic(err) }
-
This is intentionally designed in Go to be clumsy, which discourages its usage. Normally, we just need to do
log.Fatal(err)
and return in the functions.1 2 3
if err != nil { log.Fatal(err) }
Goroutines and Channels
-
Goroutines (
go
in front of function calls) enable parallelism (lightweight threads):1 2 3 4 5
func main() { go a() go b() // can't deal with the return value time.Sleep(time.Second) // sleep for 1 second to let the routines finish }
-
Channels to enable the communication between goroutines.
1 2 3 4 5 6 7 8 9
func greeting(myChannel chan string) { myChannel <- "hi" // time.Sleep(time.Second) to synchronize } func main() { myChannel := make(chan string) go greeting(myChannel) fmt.Println(<-myChannel) // receives "hi" }
Testing
-
Run
go test
1 2 3
func TestFoo(t *testing.T) { t.Error("test failed") }