# Learning Go ![rw-book-cover](https://m.media-amazon.com/images/I/81QaVrv6p4L._SY160.jpg) ## Metadata - Author: [[Jon Bodner]] - Full Title: Learning Go - Category: #golang ## Highlights - Go project is called a module. A module is not just source code. It is also an exact specification of the dependencies of the code within the module. Every module has a go.mod file in its root directory. Running go mod init creates this file for you. ([Location 267](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=267)) - The go.mod file declares the name of the module, the minimum supported version of Go for the module, and any other modules that your module depends on. ([Location 274](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=274)) - You shouldn’t edit the go.mod file directly. Instead, use the go get and go mod tidy commands to manage changes to the file. ([Location 276](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=276)) - The main package in a Go module contains the code that starts a Go program. ([Location 299](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=299)) - Go imports only whole packages. You can’t limit the import to specific types, functions, constants, or variables within a package. ([Location 305](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=305)) - if you want to store it in a different location, use the -o flag. For example, if you wanted to compile the code to a binary called “hello,” you would use the following: $ go build -o hello ([Location 328](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=328)) - The Go development tools include a command, go fmt, which automatically fixes the whitespace in your code to match the standard format. ([Location 351](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=351)) - $ go fmt ./... hello.go Using ./... tells a Go tool to apply the command to all the files in the current directory and all subdirectories. ([Location 357](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=357)) - The go fmt command won’t fix braces on the wrong line because of the semicolon insertion rule. Like C or Java, Go requires a semicolon at the end of every statement. However, Go developers should never put the semicolons in themselves. ([Location 370](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=370)) - In one class of bugs, the code is syntactically valid but quite likely incorrect. The go tool includes a command called go vet to detect these kinds of errors. Add one to the program and watch it get detected. ([Location 401](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=401)) - Each possible operation is called a target. The .DEFAULT_GOAL defines which target is run when no target is specified. In this case, the default is the build target. Next you have the target definitions. The word before the colon (:) is the name of the target. Any words after the target (like vet in the line build: vet) are the other targets that must be run before the specified target runs. The tasks that are performed by the target are on the indented lines after the target. The .PHONY line keeps make from getting confused if a directory or file in your project has the same name as one of the listed targets. ([Location 544](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=544)) - Go has many types built into the language. These are called predeclared types. They are similar to types that are found in other languages: booleans, integers, floats, and strings. ([Location 636](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=636)) - Go, like most modern languages, assigns a default zero value to any variable that is declared but not assigned a value. ([Location 642](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=642)) - A Go literal is an explicitly specified number, character, or string. Go programs have four common kinds of literals. ([Location 651](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=651)) - An integer literal is a sequence of numbers. ([Location 654](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=654)) - A floating-point literal has a decimal point to indicate the fractional portion of the value. ([Location 672](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=672)) - A rune literal represents a character and is surrounded by single quotes. ([Location 683](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=683)) - in Go single quotes and double quotes are not interchangeable. ([Location 691](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=691)) - There are two ways to indicate string literals. Most of the time, you should use double quotes to create an interpreted string literal (e.g., type "Greetings and Salutations"). These contain zero or more rune literals. They are called “interpreted” because they interpret rune literals (both numeric and backslash escaped) into single characters. ([Location 706](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=706)) - The only characters that cannot appear in an interpreted string literal are unescaped backslashes, unescaped newlines, and unescaped double quotes. ([Location 716](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=716)) - If you need to include backslashes, double quotes, or newlines in your string, using a raw string literal is easier. These are delimited with backquotes (`) and can contain any character except a backquote. ([Location 719](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=719)) - Literals are considered untyped. ([Location 727](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=727)) - there are situations in Go where the type isn’t explicitly declared. In those cases, Go uses the default type for a literal; if there’s nothing in the expression that makes clear what the type of the literal is, the literal defaults to a type. ([Location 733](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=733)) - A byte is an alias for uint8; ([Location 781](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=781)) - Go integers support the usual arithmetic operators: +, -, *, /, with % for modulus. ([Location 822](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=822)) - While Go lets you use == and != to compare floats, don’t do it. Because of the inexact nature of floats, two floating-point values might not be equal when you think they should be. Instead, define a maximum allowed variance and see if the difference between two floats is less than that. ([Location 911](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=911)) - There isn’t a lot to the complex number support in Go. Go defines two complex number types. complex64 uses float32 values to represent the real and imaginary part, and complex128 uses float64 values. ([Location 929](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=929)) - But if you need to calculate a Mandelbrot set as part of a larger program, or implement a quadratic equation solver, complex number support is there for you. ([Location 1000](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1000)) - This brings us to strings. Like most modern languages, Go includes strings as a built-in type. The zero value for a string is the empty string. Go supports Unicode; as I showed “Literals”, you can put any Unicode character into a string. Like integers and floats, strings are compared for equality using ==, difference with !=, or ordering with >, >=, <, or <=. They are concatenated by using the + operator. ([Location 1016](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1016)) - Strings in Go are immutable; you can reassign the value of a string variable, but you cannot change the value of the string that is assigned to it. ([Location 1028](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1028)) - The rune type is an alias for the int32 type, just as byte is an alias for uint8. As you could probably guess, a rune literal’s default type is a rune, and a string literal’s default type is a string. ([Location 1037](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1037)) - Most languages that have multiple numeric types automatically convert from one to another when needed. This is called automatic type promotion, and while it seems very convenient, it turns out that the rules to properly convert one type to another can get complicated and produce unexpected results. ([Location 1054](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1054)) - In fact, no other type can be converted to a bool, implicitly or explicitly. If you want to convert from another data type to boolean, you must use one of the comparison operators (==, !=, >, <, <=, or >=). ([Location 1114](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1114)) - The most verbose way to declare a variable in Go uses the var keyword, an explicit type, and an assignment. It looks like this: var x int = 10 ([Location 1146](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1146)) - If the type on the righthand side of the = is the expected type of your variable, you can leave off the type from the left side of the =. Since the default type of an integer literal is int, the following declares x to be a variable of type int: var x = 10 ([Location 1151](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1151)) - Conversely, if you want to declare a variable and assign it the zero value, you can keep the type and drop the = on the righthand side: var x int ([Location 1156](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1156)) - You can declare multiple variables at once with var, and they can be of the same type: var x, y int = 10, 20 ([Location 1160](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1160)) - You can declare all zero values of the same type: var x, y int ([Location 1165](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1165)) - or of different types: var x, y = 10, "hello" ([Location 1168](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1168)) - There’s one more way to use var. If you are declaring multiple variables at once, you can wrap them in a declaration list: var (     x    int     y        = 20     z    int = 30     d, e     = 40, "hello"     f, g string ) ([Location 1172](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1172)) - When you are within a function, you can use the := operator to replace a var declaration that uses type inference. The following two statements do exactly the same thing—they declare x to be an int with the value of 10: var x = 10 x := 10 ([Location 1187](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1187)) - As with var, you can declare multiple variables at once using :=. These two lines both assign 10 to x and “hello” to y: var x, y = 10, "hello" x, y := 10, "hello" ([Location 1194](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1194)) - Using := has one limitation. If you are declaring a variable at the package level, you must use var because := is not legal outside of functions. ([Location 1209](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1209)) - In some situations within functions, you should avoid :=: When initializing a variable to its zero value, use var x int. This makes it clear that the zero value is intended. When assigning an untyped constant or a literal to a variable and the default type for the constant or literal isn’t the type you want for the variable, use the long var form with the type specified. While it is legal to use a type conversion to specify the type of the value and use := to write x := byte(20), it is idiomatic to write var x byte = 20. Because := allows you to assign to both new and existing variables, it sometimes creates new variables when you think you are reusing existing ones (see “Shadowing Variables” for details). In those situations, explicitly declare all your new variables with var to make it clear which variables are new, and then use the assignment operator (=) to assign values to both new and old variables. ([Location 1213](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1213)) - As a general rule, you should only declare variables in the package block that are effectively immutable. ([Location 1238](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1238)) - Many languages have a way to declare a value as immutable. In Go, this is done with the const keyword. ([Location 1246](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1246)) - Constants in Go are a way to give names to literals. There is no way in Go to declare that a variable is immutable. ([Location 1312](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1312)) - Constants can be typed or untyped. An untyped constant works exactly like a literal; it has no type of its own but does have a default type that is used when no other type can be inferred. A typed constant can be directly assigned only to a variable of that type. ([Location 1314](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1314)) - Another Go requirement is that every declared local variable must be read. It is a compile-time error to declare a local variable and to not read its value. ([Location 1347](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1347)) - Even though the underscore is a valid character in a variable name, it is rarely used, because idiomatic Go doesn’t use snake case (names like index_counter or number_tries). Instead, idiomatic Go uses camel case (names like indexCounter or numberTries) when an identifier name consists of multiple words. ([Location 1419](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1419)) - In many languages, constants are always written in all uppercase letters, with words separated by underscores (names like INDEX_COUNTER or NUMBER_TRIES). Go does not follow this pattern. This is because Go uses the case of the first letter in the name of a package-level declaration to determine if the item is accessible outside the package. ([Location 1430](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1430)) - The smaller the scope for a variable, the shorter the name that’s used for it. ([Location 1442](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1442)) - Since Go is strongly typed, you don’t need to do this to keep track of the underlying type. ([Location 1448](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1448)) - arrays are rarely used directly in Go. ([Location 1481](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1481)) - You cannot read or write past the end of an array or use a negative index. ([Location 1542](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1542)) - Earlier I said that arrays in Go are rarely used explicitly. This is because they come with an unusual limitation: Go considers the size of the array to be part of the type of the array. This makes an array that’s declared to be [3]int a different type from an array that’s declared to be [4]int. This also means that you cannot use a variable to specify the size of an array, because types must be resolved at compile time, not at runtime. ([Location 1551](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1551)) - you can’t use a type conversion to directly convert arrays of different sizes to identical types. ([Location 1557](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1557)) - The main reason arrays exist in Go is to provide the backing store for slices, which are one of the most useful features of Go. ([Location 1565](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1565)) - Most of the time, when you want a data structure that holds a sequence of values, a slice is what you should use. What makes slices so useful is that you can grow slices as needed. ([Location 1568](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1568)) - In Go, nil is an identifier that represents the lack of a value for some types. ([Location 1619](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1619)) - A slice is the first type you’ve seen that isn’t comparable. ([Location 1623](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1623)) - The built-in append function is used to grow slices: ([Location 1693](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1693)) - Every time you pass a parameter to a function, Go makes a copy of the value that’s passed in. Passing a slice to the append function actually passes a copy of the slice to the function. ([Location 1736](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1736)) - As you’ve seen, a slice is a sequence of values. Each element in a slice is assigned to consecutive memory locations, which makes it quick to read or write these values. ([Location 1740](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1740)) - Every slice also has a capacity, which is the number of consecutive memory locations reserved. ([Location 1744](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1744)) - The Go runtime is compiled into every Go binary. This is different from languages that use a virtual machine, which must be installed separately to allow programs written in those languages to function. ([Location 1755](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1755)) - When a slice grows via append, it takes time for the Go runtime to allocate new memory and copy the existing data from the old memory to the new. The old memory also needs to be garbage collected. ([Location 1762](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1762)) - Just as the built-in len function returns the current length of a slice, the built-in cap function returns the current capacity of a slice. ([Location 1768](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1768)) - Most of the time, cap is used to check if a slice is large enough to hold new data, or if a call to make is needed to create a new slice. ([Location 1772](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1772)) - If you know how many things you plan to put into a slice, create it with the correct initial capacity. You do that with the make function. ([Location 1823](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1823)) - While useful, neither way allows you to create an empty slice that already has a length or capacity specified. That’s the job of the built-in make function. It allows you to specify the type, length, and, optionally, the capacity. ([Location 1828](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1828)) - One common beginner mistake is to try to populate those initial elements using append: x := make([]int, 5) x = append(x, 10) The 10 is placed at the end of the slice, after the zero values in elements 0–4 because append always increases the length of a slice. The value of x is now [0 0 0 0 0 10], with a length of 6 and a capacity of 10 (the capacity was doubled as soon as the sixth element was appended). ([Location 1835](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1835)) - If you have a good idea of how large your slice needs to be, but don’t know what those values will be when you are writing the program, use make. ([Location 1916](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1916)) - append always increases the length of a slice! If you have specified a slice’s length using make, be sure that you mean to append to it before you do so, or you might end up with a bunch of surprise zero values at the beginning of your slice. ([Location 1929](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1929)) - A slice expression creates a slice from a slice. ([Location 1935](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1935)) - When you take a slice from a slice, you are not making a copy of the data. Instead, you now have two variables that are sharing memory. ([Location 1969](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=1969)) - Whenever you take a slice from another slice, the subslice’s capacity is set to the capacity of the original slice, minus the starting offset of the subslice within the original slice. ([Location 2023](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2023)) - To avoid complicated slice situations, you should either never use append with a subslice or make sure that append doesn’t cause an overwrite by using a full slice expression. ([Location 2062](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2062)) - Be careful when taking a slice of a slice! Both slices share the same memory, and changes to one are reflected in the other. Avoid modifying slices after they have been sliced or if they were produced by slicing. Use a three-part slice expression to prevent append from sharing capacity between slices. ([Location 2093](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2093)) - The function copies as many values as it can from source to destination, limited by whichever slice is smaller, and returns the number of elements copied. The capacity of x and y doesn’t matter; it’s the length that’s important. ([Location 2116](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2116)) - Also note that you don’t assign the output of copy to a variable. If you don’t need the number of elements copied, you don’t need to assign it. ([Location 2140](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2140)) - Slices aren’t the only thing you can slice. If you have an array, you can take a slice from it using a slice expression. This is a useful way to bridge an array to a function that takes only slices. ([Location 2181](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2181)) - Be aware that taking a slice from an array has the same memory-sharing properties as taking a slice from a slice. ([Location 2200](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2200)) - Use a type conversion to make an array variable from a slice. ([Location 2223](https://readwise.io/to_kindle?action=open&asin=B0CS5DY1VN&location=2223))