Notes

package

  • Go’s package control is similar to python, but it has convention,
  • package name is all writen in lower case
  • referring to some function or vars from a package, only the exported names of this package is visible from the outside. This is similar to private / public access of defined elements.

function

  • function name is starting with Capital letter.
  • type comes after variable name when passing params to a function
  • use func keyword to define a function
  • if two parameters have the same type, we can omit former ones and keep only the last one. It’s also reflect the thinking behind the design of Go - simpliy the language expression.
  • Go can return multiple results in a function, similar to python. Is it an implicit tuple type?

asignment

  • := is initialize operator (use this as much as possible)
  • = is assign operator
  • can use var to do the declaration, we can also put an assign altogether.

type cast

  • should be explicit

named return

  • there seems no big benefit to use named return (naked return)

const

  • Numeric constants are high-precision values. An untyped constant takes the type needed by its context.

factored

  • can reduce some duplicated coding, for example, var (a int = 1, b float = 0.1) and import (“fmt”, “match/cmplx”)

import

  • factored import: use parenthesis and write each imported packaged in separated lines without comma or semi-colon

loop

  • the only keyword for loop in Go is for, can use for to do the thing that while used to do. (indeed, most of the while use case can be replaced by for)
  • can not omit {} when using for, because Go don’t use newline to differentiate code blocks like python does
  • infinite loop := for {}

defer

  • seems like defer is following a push / pop stack mechanism.
  • it’s a quite interesting feature that does the flow control thing. it’s special by comparing it with other programming languages.
  • more details Defer, Panic, and Recover

panic & recover

  • seems like panic and recover are doing the Error Handling tasks.
  • panic can be propagated to outer callers and recover can catch a panic and resume the program to a normal status from a panicking status.

nil

  • NULL
  • use == to test equivalence

struct

  • use {} to initialize a defined struct type, e.g.
    1
    v := Vertex{1, 2}

array

  • arrays cannot be resized

slice

  • slice is a dynamically-sized array, e.g.

    1
    var s []int = primes[1:4]
  • slices are like references to arrays. A slice does not store any data, it just describes a section of an underlying array.

  • when talking about the index, the slice in golang is just the same as in python.
  • slice has len and cap.
  • empty slice is nil
  • The start and end indices of a slice expression are optional; they default to zero and the slice’s length (NOTE: not cap) respectively.
  • when moving slice ptr away from the head of the original underlying array, it’ll never found back the head (but if the underlying array is defined before the slice created, you can find it back by referring to the array with var naem). this is tied to the internal mechanism of slice, it’s a ptr a length element and a cap element.

examples for array & slice

  • in below example, b is an array with type [2]string (2 is calculated out from the literal, and then fixed to as the array size). While c is a slice, even the literals contains 2 elements, but the type is still dynamically sized as []string.
    1
    2
    b := [...]string{"Penn", "Teller"}
    c := []string{"Brian", "Ryan"}

range

  • for i, v := range mylist is similar to python’s for idx, val in enumerate(mylist)
  • the returning of range: the first is the index, and the second is a copy of the element at that index.

make

  • make is an initialization function that can be used to created empty array or map that are ready to use.

variant length params

  • …T

Literals

  • no matter it is a struct literals, array literals or slice literals, it’s like providing the content values when declaring it.

if

  • can not omit {} when using if and else.
  • statement can be placed before if condition to do some preparation

switch

  • statement can be placed before the variable under switch test
  • no need to write break in each branch.
  • we can leave the switch condition empty, it’s a easy way to do long if-then-else chains.

built-ins

  • fmt is similar to c’s printf and scanf.
  • in fmt.Printf(), %f, %e and %g are all formatting float numbers. But %f formats data as decimal but no exponent; %e outputs scientific notations; while %g is a balancer between %f and %e: %e for large exponents, %f otherwise.
  • %T outputs the type of a var, %v prints the default format of a var’s value.
  • %q is a double-quoted string safely escaped with Go syntax, with this we do not need the python-style ‘“{}”‘
  • fmt.Println accepts any number of params, and output them using ‘ ‘ as separator.

copy

  • like memcopy in C, Go has a copy function:
    1
    func copy(dst, src []T) int

Declaration

  • easier to understand than C family style. it’s a improvement over traditional styles, it shows the evolution of programming languages in recent years, we can see more and more languages use the way that Go uses.

rune

  • interesting name
  • it’s unicode code point.

zero value

  • similar to C, 0 for numeric types, false for boolean types, “” for string.

Closure

A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

below two examples shows what is a closure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Example 1: the adder function returns a closure. Each closure is bound to its own sum variable.

package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Example 2:

package main

import "fmt"

func fibonacci() func() int {
i, j := 0, 1
return func() int {
r := i
i = j
j += r
return r
}

}

func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}

Methods

  • Go does not have classes. However, you can define methods on types. methods are defined outside of the type struct define.
  • receiver (v Vertex) defined in func (v Vertex) Abs() float64 is similar to the member function definition in C++, that is float64 Vertex::Abs()
  • you can define methods to any type you want (not only struct type), for example float. But be careful, you can only declare a method with a receiver whose type is defined in the same package as the method.
  • defining methods for normal type receivers and pointer receivers will have different access level. the former one only has read access while the latter one has write access, it can modify the values to which the receiver points. No matter which way you’re using to define the method, both the value and pointer of this type can call this method, but the access follows how you define the receiver as describe above.
  • The leading-end of the relationship between a method and a type is different in Go. It’s more like function is an outsider (a caller), and the object of the type is the parameter that passed to it.

interface

  • An interface can either be a normal type or a pointer type.
  • interface is also a type, that’s why when declaring it use type I interface {}
  • empty interface may hold values of any type, it’s similar to C’s void* type.
  • type switch is similar to pattern matching in Scala, it’s to provide different type assertions to expect the passed-in interface can be transformed to an expected concrete value.
  • define a String() method to let Stringer interface to do printing. It’s like defining toString() method in Java.

    1
    2
    3
    func (v YourType) String() string {
    return fmt.Sprintf("something")
    }
  • if the receiver of an abstract method is defined using pointer, then when calling the interface’s method, it can only be successful when the interface is a pointer.

goroutine

  • goroutine does not map to OS thread, it’s controled by Go runtime on segmented stack, so the number of goroutines can be much larger than Java has, namely multiple goroutines map to 1 OS thread. So, set GOMAXPROCS to the number of cores on your system is a good way to maximize potential parellelism.
  • main function is an implicit goroutine, like in some other languages, the main function is an implicit thread.

channel

  • although ‘goroutine’ looks similar to ‘coroutine’ in terms of the spelling, but functionally, what channel does is more similar to coroutine. like coroutine, channel is used to do communication (send and receive message, make one party wait until the other party does some action).
  • Normally channels are synchronous; both sides of the channel will wait until the other side is ready. A buffered channel is asynchronous; sending or receiving a message will not wait unless the channel is already full.

Questions

  • Why Go make complex a built-in type? to serve what scenario?
  • [answered] Why constants cannot be declared using the := syntax?
    • Because := is a way to omit var. but when define a constant, we use keyword const, which means it’s not a var. So := can not be used here.
  • [answered] what’s the underlying implementation of creating a slice literals? Is there an array created first, and then slicing a full of it?
    • When called, make allocates an array and returns a slice that refers to that array.
  • [answered] what does cap do in a slice? why do we need to use make to create a zeroed array and a slice? (Go Slices: usage and internals)
  • why Go use panic and recover to do error handling? is it related to async programming?
  • What would it be like when walking through a bi-tree, and launching goroutines recurrently in the duration?
  • If multiple goroutines are mapped to the same thread, does it mean the execution results would always be the same no matter how much times we run this code? (due to there’s only 1 underlying thread, so the actual cpu resource follows a non-random pattern?)

References

  1. Go Tour
  2. An Introduction to Programming in Go
  3. Go by Example
  4. A good example demonstrating goroutine concurrency