Skip to content

Instantly share code, notes, and snippets.

@jaymcgavren
Last active October 16, 2022 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaymcgavren/148f3b2163a31f2a5deb2ff358441ca7 to your computer and use it in GitHub Desktop.
Save jaymcgavren/148f3b2163a31f2a5deb2ff358441ca7 to your computer and use it in GitHub Desktop.
Markdown conversion of a planning outline for my Head First Go book. It may be instructive to note there are substantial differences between this and the content of the final book.

Syntax basics

  • Compiled language

  • Who created Go, and why

    • Fast builds
    • Fast execution
    • Concurrency
    • Garbage collection
  • What Go has been used for

    • Servers
    • Docker
  • Go Playground

  • Packages (preliminary)

    • package declaration first thing in every file
    • main: Defines a standalone executable program, not a library
    • fmt: Contains functions for printing formatted output and scanning input
  • Import (preliminary)

    • import statement(s) second thing in a file
    • Must import a package before you can call its functions
    • Extra import statements are errors (for cleanliness, compilation speed)
  • Functions (preliminary)

    • main: Special function - the place execution of a standalone program begins
  • No Dumb Questions

    • Where are the semicolons?
  • Go is statically typed

  • fmt.Println()/fmt.Print()

  • fmt.Printf()

    • Common verbs:

      • %d: decimal integer
      • %f: floating-point number
      • %s: string
      • %t: boolean
      • %c: rune
      • %v: any value in a natural format
      • %T: type of any value
      • %%: literal percent sign
    • Is variadic: demo multiple verbs in one line

    • Dangers of confusing with Print() (are there any?)

  • Expression: A combination of one or more values that produces another value (omit?)

  • Strings: HFRB p6

    • Escape sequences represent otherwise invisible characters: "\t", "\n"
  • Math operations and comparisons: HFRB p6

  • Booleans

  • Variables: HFRB p7

    • Declaration:

      • var x int = 1

      • var x int

      • var x, y int

      • Determining type of variable based on right-hand expression:

        • var x = 1
        • var x, y = 1, 2
        • var x Compile error: "missing variable type or initialization"
    • Short variable declaration: :=

      • Declares AND initializes local variables
      • LOOKS like an assignment, but should not be confused with one; it's a declaration
      • Type is implicit
      • What happens if you forget the ":"
      • Conventional wisdom: use short declarations in most places. Use var only if you will be assigning the variable a value later, and its initial value is unimportant.
    • names

      • Rules are the same for variables, functions, constants, and types

      • Case sensitive

      • Begins with a letter, can have any number of additional letters and numbers

      • camelCase/CamelCase

      • Underscores are allowed, but by convention are never used

      • Go community prefers abbreviations when meaning is obvious

        • We will NOT be following that convention in this book
    • initializers

      • inferring var type from initializer
    • Assignment operators: +=, -=, *=, /=

    • Increment/decrement statements: ++, --

      • Any numeric variable (including floats)
      • Postfix-only
    • Tuple assignment

      • x, y = 1, 2
      • x, y = y, x
      • Left-hand side must have as many variables as right-hand side has values
    • Zero values

      • Varies by type: 0 for int, "" for string, etc.
      • In Go there is no such thing as an uninitialized variable
    • Types

      • Basic types

        • int int8 int16 int32 int64

          • int literal: 1
        • float32 float64

          • float64 (not float32?) literal: 1.2
          • There are other numeric types too, but int and float64 work for most purposes.
        • bool: Only 2 possible values - true or false

        • byte

        • rune

        • string

      • Can assign any numeric type to any other, but must perform a conversion first: price int = int(1.99)

        • Conversion may change representation of value, e.g. converting float to int TRUNCATES fractional part.
      • Must convert prior to comparison also: float64(myInt) > myFloat

      • Can't assign/compare incompatible types, e.g. string to int

    • Type conversions

    • Unused local variables are a compile error

  • Go Toolchain

    • Install via golang.org

      • go version to confirm installation
      • Minimum required version
    • Tools accessed through single command, go, with several subcommands.

      • go fmt
      • go run hello.go
      • go build hello.go; ./hello
  • go fmt

    • Conventional wisdom: go fmt
    • Conventional wisdom: Set editor to use tabs, not spaces.
    • goimports?
  • fmt.Scanln()

  • if

    • A boolean__condition followed by a block in braces that is executed only if condition is true

    • Boolean operators:

      • Logical negation: !
      • &&
      • ||
    • Braces required and opener must appear on same line

    • Conventional wisdom: Omit parenthesis around conditions

    • Scope: variables declared within if block visible only within that block

  • else/else if

  • for

    • Go's only loop statement

    • Has several forms:

      • for initialization; condition; post {}
      • for condition {} used like while in other languages
      • for {} is an infinite loop that can only be exited with break or return
    • Parenthesis around initialization/condition/post are a compile error

    • Braces mandatory; opening brace must appear on same line as "post" statement

    • Scope: variables declared within for block visible only within that block

  • Returning errors

  • Multiple return values

    • Result of call is a tuple

    • Can ignore one by assigning to blank identifier _

      • Ignoring error in this way is bad form, so we'll cover how to handle errors in Ch2.

Code sample, to be built up iteratively over course of chapter:

_// Guess challenges players to guess a random number._
package main

import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)

func main() {
 seconds := time.Now().Unix()
 rand.Seed(seconds)
 target := rand.Intn(100) + 1
 fmt.Println(target)

 fmt.Println("I've chosen a random number between 1 and 100.")
 fmt.Println("Can you guess it?")

 reader := bufio.NewReader(os.Stdin)
var success = false
for guesses := 0; guesses \< 10; guesses++ {
 fmt.Printf("You have %d guesses left.\n", 10-guesses)
 fmt.Print("Make a guess: ")

 input, _ := reader.ReadString('\n') _// NOTE: Ignores possible error!_
 input = strings.TrimSpace(input)
 guess, _ := strconv.Atoi(input) _// NOTE: Ignores possible error!_

if guess < target {
 fmt.Println("Oops. Your guess was LOW.")
 } else if guess \> target {
 fmt.Println("Oops. Your guess was HIGH.")
 } else {
 success = true
 fmt.Printf("Good job! You guessed it in %d guesses!\n", guesses+1)
break
 }
 }

if !success {
 fmt.Printf("Sorry, you didn't guess my number. (It was %d.)\n", target)
 }
}

Functions

  • A function is a block of statements that together perform a specific task.

  • A function can be called from elsewhere in a program, multiple times if needed.

  • A function hides its implementation details from its users.

  • Function declaration

    • Name

      • Same rules as variables and all other names
    • List of parameters

      • Can omit type from preceding parameter if following ones are the same.
      • These become local variables within function body
    • Block containing function body

      • Scope

        • Entities (including variables) declared within function are local to that function: visible only within function.
        • Entities declared outside any functions are visible in all files of package.
    • Q: "Hey, if functions can only take a particular type, how can we pass both ints and strings to fmt.Println and fmt.Printf?" A: "We'll explain the empty interface type much later in the book."

  • Function calls

    • Call arguments vs. function parameters

    • Must always provide an argument for each parameter

      • No concept of default parameter values
      • No named parameters
      • No overloading
  • Arguments always passed by value; function receives a copy of each argument

    • Problems with this:

      • If function modifies parameter, it'll modify the copy (which then gets discarded)
      • If argument consumes a lot of memory, that will double when a copy is made
    • Pointers

      • Address-of operator: &
      • Operator to get variable referenced by pointer: *
    • If argument contains some kind of reference, function may modify variables indirectly referred to by argument

      • Pointers
      • Slices (will need to defer discussion)
      • Maps
      • Functions
      • Channels
  • Detour: The main function

  • Return values

    • Must specify type

    • return

      • Last statement of function must be a return
    • Named return values

      • Each name declares local variable
  • Multiple return values

    • If assigning to variables, need to use tuple assignment

    • Can ignore a value by assigning to blank identifier _

    • Conventional wisdom:

      • If failure has only one possible cause, bool often used as last return value to indicate success

      • If many possible causes, error used as last return

        • If error is the special built-in value nil, there was no error.
        • Otherwise, something went wrong.

Note: Variadic functions can't be covered before arrays; deferring those until arrays chapter.

Packages

  • Purpose is similar to libraries or modules in other languages TGPL p41

    • Modularity
    • Encapsulation
    • Separate compilation
    • Code reuse
  • Package serves as a name space for its declarations: utf16 package's Decode() not the same function as image package's Decode().

    • To refer to names from outside the package, need to qualify the name: utf16.Decode.
    • Do NOT have to qualify names declared within same package
  • Consists of one or more .go files in a single directory

    • Each file begins with package declaration
    • Followed by list of other packages it imports
  • Creating a program

    • $GOPATH
    • $GOPATH/src/github.com/myname/hello/hello.go
    • Core packages (e.g. "fmt", "strings")
    • Test that it builds(?): go build src/github.com/myname/hello
    • Install binary to $GOPATH/bin: go install src/github.com/myname/hello
    • $GOPATH/bin/hello
    • export $PATH=$PATH:$GOPATH/bin; hello
  • Creating a package

    • Package path

      • Must be unique
      • Good example: github.com/myname/mypkg
      • $GOPATH/src/github.com/myname/mypkg/
    • One or more .go files within package directory

      • Each file must have its own import directives; not shared
    • Package name

      • Short
      • Always all lower-case
      • Last segment of package path
      • Convention: Should be short and clear
      • Does not have to be unique; packages can be locally renamed when importing to avoid collisions
    • Exported names (anything capitalized) vs. unexported

      • Package has a lexical block (like the blocks in loops or functions but without explicit braces)
      • Anything declared at package lever with a capitalized name is exported and can be referenced from outside the package
      • Anything declared at package level with a lower-case name is unexported and can only be referenced within package
    • Package-level variables

      • Accessible anywhere in package, such as within functions

      • (Omit?) When the compiler encounters a name reference, it starts with the innermost block (maybe a for loop) and works outward through enclosing blocks (function, package).

        • It stops with the first occurence of that name it finds.
        • In this way, local variables can shadow package variables of same name.
    • Constants

      • Expressions whose value is guaranteed to occur at compile time, not run time

        • This reduces the number of operations the program has to perform at run time
      • Must be a basic type: boolean, string, or numeric (numeric includes rune)

      • The results of all arithmetic, logical, and comparison operations applied to a constant are themselves constants, resulting in further speed gains

    • Conventional wisdom: Doc comment before package line:

      • Of form // Package mypkg provides conversion from groffs to widgets.

      • Long comments should use multiline comments: /* */

        • No aligning with asterisks or other fancy formatting; just plain text
        • Very long comments should go in a separate doc.go file with nothing but a package statement
    • Test that it builds: go build src/github.com/myname/mypkg

  • Using a package

    • In src/github.com/myname/hello/hello.go: import "github.com/myname/mypkg"
    • Install, with dependency: go install github.com/myname/hello
    • Test: hello
  • Remote packages

    • go get
  • godoc

  • godoc comments

  • godoc web server

Arrays and Slices

Arrays

  • A fixed-length sequence of zero or more elements of a particular type

  • Array type is written [n]T, where n is the array size and T is the type of the array's elements

  • Access individual elements with a[0], a[1], etc.

  • Get length with len(a)

  • Fixed-length; can't grow or shrink

  • Because of fixed-length, slices are preferred

  • But every slice is built on top of an array, so to understand slices we need to understand arrays first

  • Access elements via [] subscripts, from 0 through len(a)-1

  • Built-in len(a) returns number of array elements

  • Array literals

    • Zero values for all elements by default
    • We can use array literal to initialize array with a list of values: [3]int{1, 2, 3}
  • for i, v := range a {}

    • for _, v := range a {}

Slices

  • A VARIABLE-length sequence of zero or more elements of a particular type

  • Array type is written []T, where T is the type of the array's elements (It looks like an array type, but without a size)

  • Built on top of an array

    • Slice gives access to a subsequence (or perhaps all) of the element of an underlying array
    • Visual metaphor: slides under a microscope
  • Has 3 components:

    • Pointer: points to first element of array reachable through slice, which isn't necessarily the array's first element.
    • Length: number of slice elements. Retrievable via len(s).
    • Capacity: maximum length of slice, usually same as number of elements between start of slice and end of underlying array. Retrievable via cap(s).
  • Slice operator: s[i:j]

    • s can be an array variable, a pointer to an array, or another slice
    • If i is omitted, 0 is used
    • If j is omitted, len(s) is used
    • Slicing beyond cap(s) causes a panic
    • Slicing beyond len(s) extends the slice
  • append

    • Built in function that appends items to slices
    • Show copying to a new, larger slice with a different underlying array
    • MAY cause slice to refer to different underlying array than the previous, so it's a good idea to assign return value of append() back to the same slice variable append() was called with
  • copy

    • Built-in function that copies elements from one slice to another of the same type
    • Returns number of elements actually copied, which will be the smaller of the two slice LENGTHS (not capacities). But you don't have to store that return value.
  • Slice literals

    • Similar to array literals, but size not given: []int{1, 2, 3}
  • No Dumb Questions

    • Q: Why is this so complicated? A: Other languages with variable-length arrays actually go through a similar process behind the scenes when expanding an array. The difference is that Go gives you explicit control over this process, if you want it.
  • Since a slice contains a pointer to an array element, copying a slice (not to be confused with calling copy() on it) creates an alias for the underlying array

    • This means that if you pass a slice to a function, the pointer is copied, not the underlying array. The function can modify the underlying array.
  • Slices are not comparable; must compare manually (except for bytes.Equal)

  • make([]myType, len, cap)

    • Can omit cap, in which case capacity == length
  • Implementing a stack

    • Push: stack = append(stack, 1)
    • Get top element: top := stack[len(stack)-1]
    • Pop top element: stack = stack[:len(stack)-1]
  • Removing an element from the middle of a slice

    • Use copy() to slide the following elements down to fill the gap: copy(slice[3:], slice[4:]); slice = slice[:len(slice)-1]
  • Nested slices

  • Variadic functions (use slices to store parameters)

    • Accepts any number of FINAL arguments

    • Declares variable in function body to hold slice with values of specified type

      • Caller converts arguments to slice and passes that
    • Passing an entire slice as variadic arguments: a := []int{1, 2, 3}; fmt.Println(sum(a...))

  • Passing slices to variadic functions

Strings

  • A string is a slice of arbitrary bytes

  • Usually interpreted as UTF-8

  • String literal: "in double quotes"

    • Can contain escape sequences:

      • "\n"
      • "\t"
      • """
      • "\"
  • \raw string literal``

  • Immutable (read-only)

    • s := "a"; t := s; s += "b"; s == "ab"; t == "a"
  • Comparable with <, ==, etc.

  • No Dumb Questions: Why are strings immutable?

  • Runes

    • Represents a Unicode code point, usually a character

    • Actually a synonym for int32

    • Literals written within single quotes: 'a'

    • Runes != bytes

      • len(mystring) returns length in bytes, not runes
      • TODO How to get length in runes
  • Using range loop on a string performs UTF-8 decoding implicitly

  • Produce a slice of runes by applying a []rune("foo") conversion on a string

    • Can apply a string conversion to a slice of runes as well
  • Important packages

    • bytes

    • strings

    • strconv

      • Converts between numeric values and their string representations
    • unicode

Maps

  • An unordered collection of key/value pairs in which all keys are unique

  • Written map[K]V, where K is the type of the keys and V is the type of the values

  • Keys must all be of same type, and values must all be of same type, but keys can be of different type than values

  • Key type must be comparable using ==

  • Creating a map

    • grades := make(map[string]float64)
    • grades := map[string]float64{"Bob": 64.2, "Alice": 85.9}
  • Access key values through usual subscript notation, similar to arrays and slices: grades["Alice"] = 92.5; fmt.Println(grades["Alice"])

  • delete(grades, "Bob") (built-in)

  • Accessing nonexistent keys returns zero value for value type: votes["Amber Graham"] += 1

    • If you need to know whether value is already present: count, ok := votes["Amber Graham"]
  • for name, grade := range grades {}

    • Order is random

      • You must sort keys explicitly, then iterate over those if you want to enumerate in order
    • Ignore values: for name := range grades {}

    • Ignore keys: for _, grade := range grades {}

  • Nested maps

Pointers

  • TGPL p32: "A variable is a piece of storage containing a value... A pointer is the address of a variable"

  • &x

    • Read aloud as "address of x"
    • var x int; &x yields a pointer to an integer variable; a of *int type (read aloud as "pointer to int")
  • Asterisk operator

  • All function arguments are pass-by-value, function parameter receives a copy of each argument

    • Because of this, it's not possible for a function to update values passed as arguments
    • But if the argument is a pointer, the function can alter the value it points to
  • The dangers of aliasing

  • Built-in function new()

    • new(T) creates unnamed variable of type T, initializes it to zero value of T, and returns its address as a *T value.
    • Just a shorthand for var dummy T; &dummy
    • Rarely used
  • nil pointer dereference causes a panic

  • http://stackoverflow.com/questions/23542989/pointers-vs-values-in-parameters-and-return-values

Pointers should be covered prior to Structs; functions can't modify a Struct unless it's passed as a reference, and it's probably significantly less efficient as well.

Structs and Types

Structs

  • A sequence of named elements, called fields, each of which has a name and a type. https://golang.org/ref/spec#Struct\_types

  • Fields accessed through dot notation: employee.Salary

  • Fields are variables, so we can:

    • Assign: employee.Salary = 50000
    • Take address: &employee.Salary
    • Dot notation works with pointers to structs: p := &employee; p.Salary equivalent to p := &employee; (*p).Salary
  • Struct type can contain a mixture of exported and unexported fields

  • Struct literals

    • Employee{ID: 1}
    • Similar syntax to arrays/slices/maps
    • Omitted fields get set to zero value for their type
  • Functions

    • Struct values can be passed as function arguments or returned from functions
    • Small structs usually passed/returned by value
    • Large structs usually passed/returned using a pointer
    • Struct must be passed using a pointer if function needs to modify it
  • Special shorthand to create, initialize, and obtain address of a struct: &Employee{Salary: 50000}

  • If all fields of a struct are comparable, the struct itself is comparable

  • Embedding a type within a struct

    • Anonymous fields: fields declared with a type but no name
    • Must be a named type or a pointer to a named type
    • If you embed a struct type within a struct, you can refer to the inner struct's fields as if they belonged to the outer struct

Types

Methods

  • Problems solved:

    • How do we tell which function to apply to a value?
  • A method is a function with a special receiver argument: https://golang.org/ref/spec#Method\_declarations

    • Method declaration looks just like an ordinary function declaration, except it has extra receiver parameter before the function name

    • Why "receiver"? Think of calling a method on an object as sending it a message.

    • No this or self, just an ordinary parameter name for the receiver

      • Conventional wisdom: The first letter of the type name is a common choice for the receiver parameter name. Use the same receiver name across all methods for a type.
  • Allows you to define new behaviors for values of a type

  • Dot notation for calling methods

    • Reciever occurs before method name, just like in the declaration
    • Compiler looks at type of receiver, then calls method with that name defined on that type
    • Don't confuse with package qualifier for exported function names; not the same thing
  • For struct types, method names can't match field names, it's a compile error

  • Methods can be associated with any named type, not just those based on structs

    • Can only declare methods on types defined in same package (no defining methods on int)
  • Method with value receiver

  • Method with pointer receiver (more common)

    • As with function parameters, there's no point in a method modifying its receiver parameter; it's a copy
    • Solution: take a pointer as the receiver parameter
    • Conventional Wisdom: if ANY of a type's methods have a pointer receiver, ALL methods should be converted to have a pointer receiver to avoid confusion
    • Methods with pointer receivers can take either value or pointer when called. Same for methods with value receivers. Go auto-converts.
  • All of a type's methods should have either value or pointer receivers, not a mixture.

Embedded types

  • As mentioned above, you can embed one struct within another struct and access the inner struct's fields as if they belong to the outer struct

  • You can do the same with methods: access an embedded type's methods as if they belonged to the enclosing struct

  • This is NOT inheritance

    • Embedded type is not a "base class", enclosing struct is not a "subclass"
    • There is no "is a" relationship, only "has a"
    • Attempting to assign values of the enclosing type to variables or function arguments that expect the embedded type is a compile error

Encapsulation

  • Variables or methods of an object that are inaccessible to clients of the object are said to be encapsulated

    • Because clients can't modify the object's variables, one can limit the possible values of those variables
    • You can hide inessential implementation details, allowing you to change them later without breaking clients
  • Only struct-based types allow for encapsulation

    • Just give a field a lower-case name to make it unexported
    • The unit of encapsulation is the package, not the type as with many other languages
  • Getters and Setters

    • Methods that merely access or modify unexported fields

    • Conventional wisdom:

      • Getter method for a field x is usually just named X (unlike Java etc.)
      • Setter method for field x usually named SetX

Interfaces

  • All the types we've shown thus far have been concrete types

    • TGPL p171: When you have a concrete type, you know exactly what it IS and what it can DO
    • An interface is an abstract type
    • When you have a value of an interface type, you have no idea what it IS; you only know what it can DO (a method set that it provides)
  • Substitutability: the freedom to substitute one type for another that provides the same interface

  • "Structural typing is compile-time duck typing" http://blog.carbonfive.com/2012/09/23/structural-typing-compile-time-duck-typing/

  • fmt.Stringer

    • One of the most widely-used interfaces
    • Consists of one method, String()
  • Interface type: specifies a set of methods that a concrete type must possess to be considered an instance of that interface

  • A type satisfies an interface if it possesses all methods the interface requires

  • Assignability rule for interface types

  • When variable is of an interface type, only the interface's methods can be called, even if the concrete type assigned to it has other methods

  • io.Writer

    • Abstraction of all the types to which bytes can be written (files, memory buffers, network connections, and more)
    • Consists of one method, Write()
  • io.Reader

    • All the types from which bytes can be read
    • Consists of one method, Read()
  • The empty interface type: interface{}

    • fmt.Print takes empty interface type values
    • Method list these types must satisfy is empty, in other words, they can have any methods at all
    • That means ALL types satisfy the empty interface; fmt.Print can take values of any type
  • sort.Interface

  • error interface

Recovering from failure

  • TGPL: "When a function call returns an error, it's the callers responsibility to check it and take appropriate action."

  • Errors (TODO this will have to be introduced earlier than this. Need to determine where.)

    • Tuples as return values
    • error type
    • Error()
  • Conventional wisdom: Usually deal with failure case before success. If you simply return the error case from the function, then the success case can follow the return without being indented in an if or else block.

  • TGPL: Error handling strategies:

    • Propagate the error (most common)

      • "Error messages are frequently chained together. Strings should not be capitalized and newlines should be avoided." (To aid tools like grep.)
      • Conventional wisdom: Omit return value names when meaning of return value is obvious (e.g. error)
      • fmt.Errorf()
    • For errors representing transient problems, retry the operation, possibly after delay and with a limit on attempt count.

    • If progress is impossible, print error and stop program entirely (usually only from "main" package, not libraries).

      • log.Fatalf
    • Log error and continue

      • log.Printf()
      • fmt.Printf(os.Stderr...)
    • In rare cases, ignore error entirely

  • defer

    • TGPL p 125: Don't assume Go will release unused system resources like open files and network connections. They should be closed explicitly.

    • Ordinary function or method call prefixed with keyword defer

    • Actual call gets deferred until function that contains defer statement finishes:

      • ...normally
      • ...abnormally, by panicking
    • Often used with paired operations like opening and closing a file or connecting and disconnecting from a server

      • defer a call to release a resource right after the resource is successfully required
    • https://blog.golang.org/defer-panic-and-recover

    • close refrigerator/turn oven off would make a great example!

  • panic

    • Some programmer mistakes like an out-of-bounds array access or a nil pointer dereference can only be discovered at runtime. When Go detects such a mistake it panics.

      • Normal execution stops

      • Deferred function calls are executed

      • Program crashes with a log message including:

        • Error message
        • Stack trace
    • You can call built-in panic() yourself

      • Should be reserved for "impossible" situations
      • "Expected" errors should be dealt with using error values
    • "Intentionally clumsy, rarely used, and not integrated into the library..." https://talks.golang.org/2012/splash.article#TOC\_16.

  • recover

  • Convention: Must* methods panic instead of returning error

  • Handling errors within function

  • Propagating errors to caller

    • Error messages often get chained together, so don't capitalize message strings and keep everything on one line.

Unit tests

  • Why test?
  • _test.go files
  • "testing" package
  • Running a test
  • "Table-driven" tests
  • Benchmarking?

Concurrency

  • Goroutines

  • Unbuffered channels

    • <- c blocks until value is sent
    • c <- value blocks until value is received
  • Advanced topic: Buffered channels

  • select

  • Timeouts

Building a command line app

  • "flag" package

  • "os" package

  • "io" package

  • log.Fatalf

  • Program structure

    • Files within a package
    • Extensive package doc comments placed in separate "doc.go" file

Building a web app

  • "http" package
  • Handler functions
  • Request object
  • Response writer
  • "html/template" package

Omitted topics (some will go in "what we didn't cover" appendix)

  • sized/unsigned integer/float types

  • regexp package

  • switch

  • Bare returns (assigning to named return value variables and then calling return w/o arguments)

  • May have to cover these in web app chapter:

    • Function values (First-class functions/higher-order functions)
    • Anonymous functions
    • Closures
  • Lifetime of variables: HFGO p35

  • init functions

  • Embedding interfaces

  • Interface values

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment