Function calls and Types

Function calls

A function is a chunk of code that you can call from other places in your program.
When calling a function, you can use arguments to provide the function with data.

Types

Values in Go are classified into different types, which specify what the values can be used for.
Math operations and comparisons between different types are not allowed, but you can convert a value to a new type if needed.
Go variables can only store values of their declared type.

BULLET POINTS

  • A package is a group of related functions and other code.
  • Before you can use a package’s functions within a Go file, you need to import that package.
  • A string is a series of bytes that usually reprsent text characters.
  • a rune represents a single text character.
  • Go’s tow most common numeric types are int which holds integers, and float64 which holds floating-point numbers.
  • The bool type holds Boolean values, which are either true or false.
  • A variable is a piece of storage that can contain values of a specified type.
  • If not value has been assigned to a variable, it will contain the zero value for its type. Examples of zero values include 0 for int or float64 variables, or "" for string variables.
  • You can declare a variable and assign it a value at the same time using a := short variable declaration.
  • A variable, function, or type can only be accessed from code in other packages if its name begins with a capital letter.
  • The go fmt command automatically reformats source files to use Go standard formatting. You should run go fmt on any code that you plan to share with others.
  • The go build command compiles Go source code into a binary format that computers can executes.
  • The go run command compiles and runs a program without saving an executable file in the current directory.

Conditionals and Loops

Conditionals

Conditionals are state ments that cause a block of code to be executed only if a condition is met.
An expression is evaluated, and if its result is true, the code in the conditional block body is executed.
Go supports multiple branches in the condition. These statements take the form if ... else if ... else.

Loops

Loops cause a block of code to execute repeatedly.
One common kind of loop starts with the keyword for, followed by an init statement that initializes a variable, a condition expression that determines when to break out of the loop, and a post statement that runs after each iteration of the loop.

BULLET POINTS

  • A method is a kind of function that’s associated with values of a given type.
  • Go treats everything from a // marker to the end of the line as a comment and ignores it.
  • Multiline comments start with /* and end with */. Everything in between, including newlines, is ignored.
  • It’s conventional to inculde a comment at the top of every program, explaining what it does.
  • Unlike most programming languages, Go allows multiple return values from a function or method call.
  • One common use of multiple return values is to return the function’s main result, and then a second value indicating whether there was a error.
  • To discard a value without using it, use the _ blank identifier. The blank identifier can be used in place of any variable in any assignment statemnet.
  • Avoid giving variables the same name as types, functions, or packages; it causes the variable to shadow(override) the item with the same name.
  • Functions, conditionals, and loops all have blocks of code that appear within {} braces.
  • Their code doesn’t appear within {} braces, but files and packages also comprise blocks.
  • The scope of a variable is limited to the block it is defined within, and all blocks nested within that block.
  • In addition to a name, a package may have an import path that is required when it is imported.
  • The continue keyword skips to the next iteration of a loop.
  • The break keyword exists out of a loop entirely.

Function declarations and Pointers

Function declarations

You can declare your own functions, and then call them elsewhere in the same package by typing the function name, followed by a pair of parentheses containing the arguments the function requires (if any).
You can declare that a function will return one or more values to its caller.

Pointers

You can get a pointer to a variable by typing Go’s “address of” oprator (&) right before the variable name: &myVariable.
Pointer types are written with a * followed by the ytpe of value the pointer points to (*int, *bool, etc…).

BULLET POINTS

  • The fmt.Printf and fmt.Sprintf functions format values they’re given. The first argument should be a formatting strign containing verbs (%d, %f, %s, etc.) that values will be substituted for.
  • Within a formatting verb, you can inculde a width: a minimum number of characters the formatted value will take up. For example, %12s results in a 12-character string (padded with spaces), %sd results in a 2-character integer, and %.3f results in a floating-point number rounded to 3 decimal places.
  • If you want calls to your function to accept arguments, you must declare one or more parameters, inculding types for each, in the function declaration. The number and type of arguments must always match the number and type of parameters, or you’ll get a compile error.
  • If you want your function to return one or more values, you must declare the return value types in the function declaration.
  • You can’t access avriable declared within a function outside that function. But you can access a variable declared outside a function (usually at the package level) within that function.
  • When a function returns multiple values, the last value usually has a type of error. Error values have an Error() method that returns a string describing the error.
  • By convention, functions return an error value of nil to indicate there are no errors.
  • You can access the value a pointer as a parameter, and it updates the value at that pointer, then the updated value will still be visible outside the function.

Packages

The Go workspace is a special directory on your computer that holds Go code.
You can set up a package for your programs to use by creating a directory in the workspace that contains one or more source code files.

BULLET POINTS

  • By default, the workspace directory is a directory named go within your user’s home directory.
  • You can use another directory as your workspace by setting the GOPATH environment variable.
  • Go uses three subdirectories within the workspace: the bin directory holds compiled executable programs, the pkg directory holds compiled package code, and the src directory holds Go source code.
  • The names of the directories within the src directory are used to form a package’s import path. Names of nested directories are separated by / characters in the import path.
  • The package’s name is determined by the package clauses at the top of the source code files within the package directory. Except for the main package, the package name should be the same as the name of the directory that contains it.
  • Package names should be all lowercase, and ideally consist of a single word.
  • A package’s functions can only be called from outside that pacakge if they’re exported. A function is exported if its name begins with a capital letter.
  • A constant is a name referring to a value that will never change.
  • The go install command compiles a package’s code and stores it in the pkg directory for general packages, or the bin directory for executable programs.
  • A common convention is to use the URL where a package is hosted as its import path. This allows the go get tool to find, download, and install packages given only their import path.
  • The go doc tool displays documentation for packages. Doc comments within the code are inculded in go doc’s output.

Arrays

An array is a list of values of a particular type.
Each item in an array is referred to as an array element.
An array holds a specific number of elements; no means ar available to easily add more elements to an array.

BULLET POINTS

  • To declare an array variable, include the array length in square brackets and the type of elements it will hold: var myArray [3]int
  • To assign or access an element of an array, provide its index in square brackets. Indexes start at 0, so the first element of myArray is myArray[0]
  • As with variables, the default value for all array elements is the zero value for that element’s type.
  • You can set element values at the time an array is created using an array literal: [3] int{4, 9. 6}
  • If you store an index that is not valid for an array in a variable, and then try to access an array element using that variable as an index, you will get a panic – a runtime error.
  • You can get the number of elements in an array with the build-in len function.
  • You can conveniently process all the elements of an array using the special for ... range loop syntax, which loops through each element and assigns its index and value to variables you provide.
  • When using a for ... range loop, you can ignore either the index or value for each element by assigning it to the _ blank identifier.
  • The os.Open function opens a file. It returns a pointer to an os.File value representing that opened file.
  • Passing an os.File value to bufio.NewScanner returns a bufio.Scanner value whose Scan and Text methods can be used to read a line at a time from the file as strings.

Slices

A slice is also a list of elements of a partifular type, but unlike arrays, tools are available to add or remove elements.
Slice don’t hold any data themselves.
A slice is merely a view into the elements of an underlying array.

BULLET POINTS

  • The type for a slice variable is declared just like the type for an array variable, except the length is ommited: var mySlice []int
  • For the most part, code for working with slices is identical to code that works with arrays. This includes: accessing elements, using zero values, passing slices to the len function, and for … range loops.
  • A slice literal looks just like an array literal, except the length is ommited: []int{1, 7, 10}
  • You can get a slice that contains elements i throgh j-1 of an array or slice using the slice operator: s[i:j]
  • The os.Args package variable contains a slice of strings with the command-line arguments the current program was run with.
  • A variadic function is one that can be called with a varying number of arguments.
  • To declare a variadic function, place an ellipsis(…) before the type of the last parameter in the function declaration. That parameter will then receive all the variadic arguments as a slice.
  • When calling a variadic function, you can use a slice in place of the variadic arguments by typingan ellipsis after the slice: inRange(1, 10, mySlice...)

Maps

A map is a collection where each value is stored under a corresponding key.
Whereas arrays and slices can only use integers as indexes, a map can use (almost) any type for keys.
All of a map’s key must be the same type, and all the values must be the same type, but the keys don’t have to be the same type as the values.

BULLET POINTS

  • When declaring a map variable, you must provide the types for its keys and its values: var myMap map[string]int
  • To create a new map, call the make function with the type of the map you want: myMap = make(map[string]int)
  • To assign a value to a map, provide the key you want to assign it to in square bracket: myMap["my key"] = 12
  • To get a value, you provide the key as well: fmt.Println(myMap["my key"])
  • You can create a map and initialize it with data at the same time using a map literal: map[string]int{"a": 2, "b": 3}
  • As with arrays and slices, if you access a map key that hasn’t been assigned a value, you’ll get a zero value back.
  • Getting a value from a map can return a second, optional Boolean value that indicates whether that value was assigned, or if it represents a default zero value: value, ok := myMap["c"]
  • If you only want to test whether a key has had a value assigned, you can ignore the actual value using the _ blank identifier: _, ok = myMap["c"]
  • You can delete keys and their corresponding values from a map using the delete build-in function: delete(myMap, "b")
  • You can use for ... range loops with maps, much like you can with arrays or slices. You provide one variable that will be assigned each key in tur, and a second variable that will be assigned each value in turn.
    1
    2
    3
    for key, value := range myMap { 
    fmt.Println(key, value)
    }

Structs and Defined types

Structs

A struct is a value that’s constructed by joining together other values of different types.
The separate values that form a struct are known as fields.
Each field has a name and a type.

Defined Types

Type definitions allow you to create new types of your own.
Each defined type is based on an underlying type that determines how values are stored.
Defined types can use any type as an underlying type, although structs are most commonly used.

BULLET POINTS

  • You can declare a variable with a struct type. To specify a struct type, use the struct keyword, followed by a list of filed names and types within curly braces.
    1
    2
    3
    4
    var myStruct struct {
    field1 string
    field2 int
    }
  • Writing struct types repeatedly can get tedious, so it’s usually best to define a type with an underlying struct type. Then the defined type can be used for variables, function parameters or return values, and so on.
    1
    2
    3
    4
    type myType struct {
    field1 string
    }
    var myVar myType
  • Struct fields are accessed via the dot operator.
    1
    2
    myVar.field1 = "value"
    fmt.Println(myVar.field1)
  • If a function needs to modify a struct or if a struct is large, it should be passed to the function as a pointer.
  • Types will only be exported from the package they’re defined in if their name begins with a capital letter.
  • Likewise, struct fields will not be accessible outside their package unless their name is capitalized.
  • Struct literals let you create a struct and set its fields at the same time. myVar := myType{field1: "value"}
  • Adding a struct field with no name, only a type, defines an anonymous field.
  • An inner struct that is added as part of an outer struct using an anonymous field is said to be embedded within the outer struct.
  • You can access the fields of an embedded struct as if the belong to the outer struct.

Method definitions

A method definition is just like a function definition, except that it includes a receiver parameter.
the method becomes assiociated with the type of the receiver parameter.
From then on, that method can be called on any value of that type.

BULLET POINTS

  • Once you’ve defined a type, you can do a conversion to that type from any value of the same underlying type: Gallon(10.0)
  • Once a variable’s type is defined, values of other types cannot be assigned to that variable, even if they have the same underlying type.
  • A defined type supports all the same operators as its underlying type. A type based on int, for example, would support +, -, *, /, ==, >, < operators.
  • A defined type can be used in operations together with literal values: Gallons(10.0) + 2.3
  • To define a method, provide a receiver parameter in parentheses before the method name: func (m MyType) MyMethod() {}
  • The receiver parameter can be used within the method block like any other parameter:
    1
    2
    3
    func (m MyType) MyMethod() {
    fmt.Println("called on", m)
    }
  • You can define additional parameters or return values on a method, just as you would with any other function.
  • Defining multiple functions with the same name in the same package is not allowed, even if the have parameters of different types. But you can define multiple methods with the same name , as long as each is defined on a different type.
  • You can only define methods on types that were defined in the same package.
  • As with any other parameter, receiver parameters receive a copy of the original value. If your method needs to modify the receiver, you should use a pointer type for the receiver parameter, and modify the value at that pointer.

Encapsulation and Embeding

Encapsulation

Encapsulation is the practice of hiding data in one part of a program from code in another part.
Encapsulation can b used to protect against invalid data.
Encapsulated data is also easier to change. You can be sure you won’t break other code that accesses the data, because no code is allowed to.

Embedding

A type that is stored within a struct type using an anonymous field is said to be embedded within the struct.
Methods of an embedded type get promoted to the outer type. They can be called as if they were defined on the outer type.

BULLET POINTS

  • In Go, data is encapsulated within packages, using unexported package variables or struct fields.
  • Unexported variables, struct fields, functions, methods, and the like can still be accessed by exported functions and methods defined in the same package.
  • The practice of ensuring that data is valid before accepting it is known as data validation.
  • A method that is primarily used to set the value of an encapsulated field is known as a setter method. Setter methods often include validation logic, to ensure the new value being provided is valid.
  • Since setter methods need to modify their receiver, their receiver parameter should have a pointer type.
  • It’s conventional for setter method names to be in the form SetX where X is the name of the field being set.
  • A method that is primarily used to get the value of an encapsulated field is known as a getter method.
  • It’s conventional for getter method names to be in the form X where X is the name of the field being set. Some other programming languages favor the form GetX for getter method names, but you should not use that form in Go.
  • Methods defined on an outer struct type live alongside methods promoted from an embedded type.
  • An embedded type’s unexported methods don’t get promoted to the outer type.

Interface

An interface is a set of methods certain values are expected to have.
Any type that has all the methods listed in an interface definition is said to satisfy that interface.
A type that satisfies an interface can be assigned to any variable or function parameter that uses that interface as its type.

BULLET POINTS

  • A concrete type specifies not only what its values can do (what methods you can call on them), but also what they are: they specify the underlying type that holds the value’s data.
  • An interface type is an abstract type. Interfaces don’t describe what a value is: they don’t say what its underlying type is or how its data is stored. They only describe what a value can do: what methods it has.
  • An interface definition needs to contain a list of method names, along with any parameters or return values those methods are expected to have.
  • To satify an interface, a tyep must have all the methods the interface specifies. Method names, parameter types (or lack thereof), and return value types (or lack thereof) all need to match those defined int the interface.
  • A type can have methods in addition to those listed in the interface, but it mustn’t be missing any, or it doesn’t satisfy that interface.
  • A type can statisfy multiple intercaces, and an interface can have multiple types that satisty it.
  • Interface statisfaction is automatic. There is no need to explictily declare that a concrete type satisfies an interface in Go.
  • When you have a variable of an interface type, the only methods you can call on it are those defined in the interface.
  • If you’ve assigned a value of a concrete type to a variable with an interface type, you can use a type assertion to get the concrete type value back. Only then can you call methods that are defined on the concrete type (but not the interface).
  • Type assertion return a second bool value that indicates whether the assertion was successful. car, ok := vehicle.(Car)

Defer and Recover

Defer

The defer keyword can be added before any function or method call to postpone that can until the current function exists.
Defered function calls are ofter used for cleanup code that needs to be run even in the event of an error.

Recover

If a deferred function calls the built-in recover function, the program will revcover from a panic state (if any).
The recover function returns whatever value was originally passed to the panic function.

BULLET POINTS

  • Retruning early from a function with an error value is a good way to indicate an error has occurred, but it can prevent cleanup code later in the function from being run.
  • You can use the defer keyword to call your cleanup function immediately after the code that requires cleanup. That will set up the cleanup code to run when the current function exits, wheter or not there was an error.
  • You can call the built-in panic function to cause your program to panic.
  • Unless the built-in recover function is called, a panicking program will crash with a log message.
  • You can pass any value as an arguement to panic. That value will be coverted to a string and printed as part of the log message.
  • A panic log message includes a stack trace, a list of all active function calls that can be useful for debugging.
  • When a program panics, any deferred function calls will still be made, allowing cleanup code to be executed before a crash.
  • Defferred functions can also call the built-in recover function, which will cause the program to resume normal execution.
  • If recover is called when there is no panic, it simply returns nil.
  • If recover is called during a panic, it returns the value that was passed to panic.
  • Most programs should panic only in the event of an unanticipated error. You should think about all possible errors your program might encounter (such as missing files or badly formatted data), and handle those using error values instead.

Goroutines and Channels

Goroutines

Goroutines are functions that are run concurrently.
New goroutines are started with a go statement: an ordinary function call preceded by the go keyword.

Channels

A channel is ad data structure used to send values between goroutines.
By default, sending a value on a channel blocks (pauses) the current goroutine until that value is received.
Attempting to receive a value also blocks the current goroutine until a value is sent on that channel.

BULLET POINTS

  • All Go programs have at least one goroutine: the one that calls the main function when the program starts.
  • Go programs end when the main goroutine stops, even if other goroutines have not completed their work yet.
  • The time.Sleep function paused the current goroutine for a set amount of time.
  • Go makes no guarantees about when it will switch between goroutines, or how long it will keep running one goroutine for. This allows the goroutines to run more efficiently, but it means you can’t count on operations happening in a particular order.
  • Function return values can’t be used in a go statement, in part because the return value wouldn’t be ready when the calling function attempted to use it.
  • If you need a value from a goroutine, you’ll need to pass it a channel to send the value back on.
  • Channels are created by calling the built-in make function.
  • Each channel only carries values of one particular type; you specify that type when creating the channel. myChannel := make(chan MyType)
  • You send values to channels using the <- operator: myChannel <- "a value"
  • The <- operator is also used to receive values from a channel: value := <- myChannel

Testing

An automated test is a separate program that executes components of your main program, and verifies they behave as expected.
Go inculdes a testing package you can use to write automated tests for your code, and a go test command you can use to run those tests.

BULLET POINTS

  • An automated test runs your code with a particular set of inputs, and looks for a particular result. If the code’s output matches the expected value, the test will “pass”; otherwise, it will “fall”.
  • The go test tool is used to run tests. It looks for files within a specified package whose names end in _test.go.
  • You’re not required to make your tests part of the same package as the code you’re testing, but doing so will allow you to access unexported types or functions from the package.
  • Tests are required to use a type from the testing package, so you’ll need to import that package at the top of each test file.
  • A _test.go file can contain one or more test functions, whose names begin with Test. The rest of the name can be whatever you want.
  • Test functions must accept a single parameter: a pointer to a testing.T value.
  • Your test code can make ordinary calls to the functions and methods in your package, then check that the return values match the expected values. If they don’t, the test should fail.
  • You can report that a test has failed by calling methods (such as Error) on the testing.T value. Most methods accept a string with a message explaining the reason the test failed.
  • The Errorf method works similarly to Error, but it accepts a formatting string just like the fmt.Printf function.
  • Functions within a _test.go file whose names do not begin with Test are not run by go test. They can be used by tests as “helper” functions.
  • Table-driven tests are tests that process “table” of inputs and expected outputs. They pass each set of input to the code being tested, and check that the code’s output matches the expected values.

HTTP handler functions and First-class functions

HTTP handler functions

A net/http handler function is one that has been set up to handle browser requests for a certain path.
A handler function receives an http.ResponseWriter value as a parameter.
The handler function should write a response out using the ResponseWrite.

First-class functions

In a language with first-class functions, functions can be assigned to variables, and then called later using those variables.
Functions can also be passed as arguments when calling other functions.

BULLET POINTS

  • The net/http package’s ListenAndServe function runs a web server on a port you specify.
  • The localhost hostname handles connections from your computer back to itself.
  • Each HTTP request includes a resource path, which specifies which of a server’s many resources the browser is requesting.
  • The HandleFunc function takes a path string, and a function that will handle request for that path.
  • You can call HandleFunc repeatedly to set up different handler functions for different paths.
  • Handler functions must accept an http.ResponseWriter value and a pointer to an http.Request value as parameters.
  • If you call the Write method on an http.ResponseWriter with a slice of bytes, that data will be added to the response sent to the browser.
  • Variables that can hold a function have a function type.
  • A function type includes the number and type of parameters that the function accepts (or lack thereof), and the number and type of values that the function returns (or lack thereof).
  • If myVar holds a function, you can call that function by putting parenthese (containing any arguments the function might require) after the variable name.

Templates

The text/template package takes a template string (or a template loaded from a file) and inserts data into it.
The html/template package works just like text/template, except that it also provides security protections needed for working with HTML.

BULLET POINTS

  • A template string contains text that will be output verbatim. Within this text, you can insert various actions containing simple code that will be evaluated. Actions can be used to insert data into the template text.
  • A Template value’s Execute method takes a value that satisfies the io.Writer interface, and a data value that can be accessed within actions in the template.
  • HTTP GET requests are commonly used when a browser needs to get data from the server.
  • HTTP POST requests are used when a browser needs to submit new data to the server.
  • From data from a request can be accessed using an http.Request value’s FormValue method.
  • The http.Redirect function can be used to direct the browser to request a different path.

More

Initialization statements for “if”

1
2
3
if count := 5; count > 4 {
fmt.Println("count is", count)
}

The switch statment

1
2
3
4
5
6
7
8
9
10
11
switch rand.Intn(3) + 1 {
case 1:
fmt.Println("1")
case 2:
fallthrough
case 3:
fmt.Println("higher than 1")
default:
panic("invalid door number")
}

More basic types

int, int8, int16, int32, int64
unit, unit8, unit16, unit32, unit64
float32, float64

More about runes

Working with partial strings safely means converting the string to runes, not bytes.

Buffered channels

1
channel := make(chan string, 3)

Futher Reading

A Tour of Go
Effective Go
Package Documentation