Irbisa · Cheatsheet

Go Basics

Syntax, zero values, constants and iota, control flow, defer and closures

Junior Developer · 25 questions

  1. 01

    What is Go, why was it created, and what are its main strengths and weaknesses?

    Go is a statically typed, compiled language with garbage collection, created at Google in 2007 by Robert Griesemer, Rob Pike and Ken Thompson to build large networked servers with fast builds and simple, readable code.

  2. 02

    Which familiar features does Go leave out on purpose (ternary operator, while, inheritance, exceptions, overloading) and what does it use instead?

    Go leaves out features that add a second way to write the same thing or hide control flow, and each has one plain replacement.

  3. 03

    What is a package in Go, how do you import one, and what makes a name exported?

    A package is all the .go files in one directory that share the same package clause.

    // store/store.go, in module example.com/apppackage store type Row struct {	ID   int    // exported: visible to other packages and to encoding/json	name string // unexported: visible only inside package store…
  4. 04

    What is the difference between go run, go build and go install?

    All three compile the same way and differ only in where the binary goes: go run builds it in a temporary directory, runs it and throws it away; go build writes it to the current directory (or to -o); go install puts it in $GOBIN, by default ~/go/bin.

  5. 05

    What are the ways to declare a variable in Go, and where can you use :=?

    var declares a variable anywhere, with a type, an initial value or both; the short form := declares and infers the type in one step but works only inside a function body.

    var build = "dev" // package level: `build := "dev"` here is a syntax error func load(path string) error {	var rows []Row    // no value yet: the zero value (a nil slice) is what we want	var id uint64 = 7 // := would have inferred int…
  6. 06

    What is a zero value in Go, and what is it for numbers, strings, pointers, slices, maps, channels and structs?

    A zero value is what a variable holds when it is declared without an initialiser: 0 for numbers, false for bool, "" for strings, nil for pointers, slices, maps, channels, functions and interfaces.

    type Server struct {	Addr string          // ""	Port int             // 0	mu   sync.Mutex      // ready to use, no constructor	buf  bytes.Buffer    // same	tags []string        // nil…
  7. 07

    What are constants and iota in Go, and how do you write an enum?

    A constant is a value fixed at compile time (a number, string, boolean or rune) declared with const.

    type State uint8 const (	StateUnknown State = iota // 0, also the zero value of State	StatePending              // 1: the line repeats `State = iota`	StateRunning              // 2…
  8. 08

    What size is int in Go, and what happens on integer overflow and on division by zero?

    int and uint are 64 bits on 64-bit platforms and 32 bits on 32-bit ones.

    func main() {	defer func() { fmt.Println("recovered:", recover()) }() 	fmt.Println(strconv.IntSize) // 64 on amd64 and arm64 	var a int8 = 127…
  9. 09

    How does type conversion work in Go, and why is there no implicit conversion between numeric types?

    Every conversion is written out as T(x): Go never converts between numeric types for you, not even int to int64, because silent conversions are a classic source of overflow and precision bugs.

    done, total := 3, 4fmt.Println(done / total * 100)                   // 0: integer division firstfmt.Println(float64(done/total) * 100)            // 0: converted too latefmt.Println(float64(done) / float64(total) * 100) // 75 v, f, n := 300, 3.9, -1…
  10. 10

    What forms does the for loop take in Go, and what can range iterate over?

    for is Go's only loop keyword, in three shapes: the three-clause for i := 0; i < n; i++, the condition-only for cond {} that plays the role of while, and the bare for {} that runs until break or return.

    for i := 0; i < 3; i++ { // three-clause	fmt.Print(i)} for rows.Next() { // condition only: Go's while	scan(rows)…
  11. 11

    What does this loop print, and how do you make the break stop the loop?

    jobs := []string{"a", "stop", "b"}for _, j := range jobs {	switch j {	case "stop":		fmt.Println("stopping")		break	default:		fmt.Println("run", j)	}}

    It prints run a, stopping, run b.

  12. 12

    How do multiple return values work in Go, and what is the blank identifier _ used for?

    A function can return several values, and by convention the last one is an error.

    func divmod(a, b int) (int, int, error) {	if b == 0 {		return 0, 0, errors.New("division by zero")	}	return a / b, a % b, nil}…
  13. 13

    What is a variadic function, and how do you pass a slice to one?

    A variadic function takes any number of arguments of one type through its last parameter, as in func sum(xs ...int) int; inside the function xs is a plain []int.

    func sum(xs ...int) int {	total := 0	for _, x := range xs {		total += x	}	return total…
  14. 14

    How are arguments passed to functions in Go, and when should you pass a pointer instead of a value?

    Go always passes by value: the function gets a copy of each argument.

    type Config struct {	Addr    string	Timeout time.Duration} func setTimeoutCopy(c Config) { // wrong: changes a copy…
  15. 15

    What is the difference between make and new?

    new(T) works for any type: it allocates a zeroed T and returns a *T.

    p := new(int)             // *int pointing at 0u := &User{Name: "ada"}   // *User with fields set: the usual choicem := make(map[string]int) // ready for writess := make([]int, 0, 100)  // len 0, cap 100ch := make(chan int, 10)  // buffered channel…
  16. 16

    How does defer work: when do deferred calls run, in what order, and when are their arguments evaluated?

    defer schedules a call to run when the surrounding function returns, whether by return or while a panic unwinds.

    func countdown() {	for i := 0; i < 3; i++ {		defer fmt.Print(i, " ") // i is evaluated now, the call runs at return	}} // prints 2 1 0…
  17. 17

    What is the difference between fmt.Println, fmt.Printf and fmt.Sprintf, and what do %v, %+v, %#v, %T and %q print?

    fmt.Println prints its arguments separated by spaces and adds a newline; fmt.Printf formats them with a template of verbs; fmt.Sprintf formats the same way but returns the string instead of printing it.

    type User struct {	ID   int	Name string} func main() {…
  18. 18

    This helper tries several URLs and logs every failed one, yet returns nil when all of them fail. Why, and why is there no compiler warning?

    func fetch(urls []string) error {	var err error	for _, u := range urls {		if resp, err := http.Get(u); err != nil {			log.Printf("attempt %s: %v", u, err)		} else {			resp.Body.Close()			return nil		}	}	return err}

    The := in the if header declares a new resp and a new err scoped to that if, so the outer err is never assigned and the function returns its zero value, nil.

  19. 19

    Find the bug: lastN returns the right items for small n, but panics with index out of range when n equals len(xs).

    func lastN(xs []int, n uint) []int {	var out []int	for i := uint(len(xs)) - 1; i >= uint(len(xs))-n; i-- {		out = append(out, xs[i])	}	return out}

    i is a uint, so i >= uint(len(xs))-n is always true when n == len(xs) (the right side is 0); after i reaches 0, i-- wraps around to 18446744073709551615 and xs[i] panics.

  20. 20

    What does f() return, and what would it return if the result were not named?

    func f() (n int) {	defer func() {		n *= 2	}()	n = 1	return n + 2}

    It returns 6: return n + 2 assigns 3 to the named result n, then the deferred closure doubles it before the caller gets n.

  21. 21

    Find the bug: hashAll passes its tests but fails in production on large directories with too many open files.

    func hashAll(paths []string) (map[string]string, error) {	sums := make(map[string]string, len(paths))	for _, p := range paths {		f, err := os.Open(p)		if err != nil {			return nil, err		}		defer f.Close()		h := sha256.New()		if _, err := io.Copy(h, f); err != nil {			return nil, err		}		sums[p] = hex.EncodeToString(h.Sum(nil))	}	return sums, nil}

    defer f.Close() runs when hashAll returns, not at the end of each iteration, so every file stays open until the whole loop finishes; with enough paths the process runs out of file descriptors and os.Open fails.

  22. 22

    What is a closure in Go, and how did Go 1.22 change what closures capture inside a for loop?

    A closure is a function literal that uses variables of the enclosing function; it captures the variables themselves, not their values.

    func counter() func() int {	n := 0 // captured by the closure, outlives counter()	return func() int {		n++		return n	}…
  23. 23

    When does init() run relative to imports and package-level variables, and why is putting real work in it discouraged?

    init() runs once per package, after all imported packages and the package's own variables are initialised and before main.

    // lookup/lookup.gopackage lookup var a = b + 1 // dependency order, not source order: b is set first, so a is 3var b = 2…
  24. 24

    What is the difference between a type definition type UserID string and a type alias type UserID = string?

    type UserID string is a type definition: a new, distinct type with string as its underlying type and no methods of its own.

    type UserID string // a new typetype Name = string // an alias: Name and string are the same type func (id UserID) Valid() bool { return strings.HasPrefix(string(id), "u_") } type Set[T comparable] = map[T]struct{} // Go 1.24: generic alias…
  25. 25

    What has changed in Go over the last few releases, and which of those changes affect everyday code?

    For everyday code the big ones are per-iteration loop variables and range over integers (1.22), iterators (1.23), generic aliases and Swiss-table maps (1.24), container-aware GOMAXPROCS (1.25), new(expr) plus the Green Tea GC by default (1.26), and generic methods (1.27).