Course+outline+-+learn+to+code+go+-+v3 18 0
Course+outline+-+learn+to+code+go+-+v3 18 0
Course introduction 8
Welcome 8
Course resources 9
Additional course resources 10
The Power of Education to Transform Lives 10
Getting going with Go 10
Why Go? 10
Documentation & example code 12
Creating our first go program - hello gophers 😃 ! 13
Exploring format printing and documentation 14
How computers work - core principles 14
ASCII, Unicode, & UTF-8 - understanding text 14
String literals and documentation 15
Hands-on exercises 15
Hands-on exercise #1 15
The fundamentals of Go 15
Variables, zero values, blank identifier 15
Using printf for decimal & hexadecimal values 16
Numeral systems: decimal, binary, & hexadecimal 16
Values, types, conversion, scope & housekeeping 17
Built-in types, aggregate types, and composition 18
Hands-on exercises 20
Hands-on exercise #2 - go tour step 1 - 3 20
Hands-on exercise #3 - go tour step 4 - 7 20
Hands-on exercise #4 - go tour step 8 - 10 21
Hands-on exercise #5 - go tour step 11 - 13 21
Hands-on exercise #6 - go tour step 14 - 15 21
Hands-on exercise #7 - go tour step 16 - 17 - bitwise ops & bit shifting 21
Hands-on exercise #8 (was #03) - iota 21
Hands-on exercise #9 (was #04) - measuring bits with bitwise operations 21
Hands-on exercise #10 (was #05) - zero value, :=, type specificity, blank identifier 22
Hands-on exercise #11 (was #06) - printf verbs to show values and types 22
Hands-on exercise #12 (was #07) - printf binary, decimal, & hexadecimal 23
Hands-on exercise #13 (was #08) - signed and unsigned int 23
Course introduction
Welcome
● Getting resources
○ next to every video
○ “course resources” in this section
○ https://github.com/GoesToEleven/learn-to-code-go-version-03
■ WORK IN PROCESS
● Questions and answers
○ This is THE BEST AND QUICKEST place to get your questions answered
○ golang bridge forum
Todd McLeod - Learn To Code Go - Page 8
● Go vs golang
● golang playground
● Mindset
○ Be an adventurer
■ spirit of exploration
■ don't fear computers - fear ignorance
○ Practice
■ practice leads to progress
● drop by drop …
● persistently patiently …
● every day I take consistent action …
● grit … - Angela Duckworth
■ Perspective
● as you think and act …
● henry ford, whether you think you can …
● bill gates, 1 year vs 10 years …
■ Transformation
● education can transform your life
○ my life, the lives of others
● next video might potentially be the Udemy & Atlassian video
○ or I will try to link to it in this video and all of the videos in
this section
● Imposter syndrome
○ 50 - 80% of programmers feel this
○ we are always operating on the edge of understanding
■ we don't have to know the answer, we have to know how to find the
answer.
○ if you don’t believe in yourself, move forward regardless
■ the belief will come
● Band together
○ we’re in this together
■ beginners, intermediate, & advanced
■ be a teacher and a student
● we can all learn from each other
○ let’s help each other and be kind
■ post questions; answer questions
● it will help you learn
curriculum item # 001-welcome
video: 001-WELCOME-take-02.mp4
Course resources
curriculum item # 002a no video
Why Go?
● built by Google
○ Rob Pike
■ Unix, UTF-8
○ Robert Griesemer
■ Studied under the creator of Pascal
○ Ken Thompson
■ solely responsible for designing and implementing the original Unix
operating system
■ invented the B programming language, the direct predecessor to the C
programming language.
■ helped invent the C programming language
● timeline
○ 2005 - first dual core processors
○ 2006 - Go development started
○ 2009 - open sourced (november)
○ 2012 - version 1 (march) - go blog link
● Why Go
○ efficient compilation
■ Go creates compiled programs
● there is a garbage collector (GC)
● there is no virtual machine
■ fast compilation → fast development
■ compiles to a static executable
● static linking - compiles libraries into your program.
● dynamic linking loads libraries into memory when program runs
■ cross compiles to different OS’s
○ efficient execution
○ ease of programming
One of the key features of Go is its simplicity. The language has a clean syntax that is easy to
read and write, making it an ideal choice for beginners who are just starting to learn
programming. Despite its simplicity, Go is a powerful language that can handle complex tasks
and scale well, making it an excellent choice for building large-scale software projects.
Go also comes with a built-in concurrency model that makes it easy to write highly concurrent
and parallel programs. This makes it an ideal choice for building applications that need to
handle a large number of concurrent requests or perform multiple tasks simultaneously.
Go is also an open-source language, which means that it is constantly evolving and improving
with contributions from a large and active community of developers. This ensures that Go will
remain relevant and up-to-date with the latest developments in technology.
Overall, Go is a great language to learn today because it is simple, powerful, fast, concurrent,
and constantly evolving. Whether you are a beginner or an experienced programmer, Go is a
language that can help you build great software projects efficiently and effectively.
https://go.dev/play/p/dRzQghWc-gA
curriculum item # 006-package-main-func-main
video: 006-package-main-func-main-02.mp4
Hands-on exercises
Hands-on exercise #1
● print out a some text with emojis
● print out a raw string literal
https://go.dev/play/p/niidn0N9RHL
curriculum item # 011-exercise-01
video: 011-exercise-01.mp4 was 015-exercise-01.mp4
The fundamentals of Go
if x := f(); x < y {
return x
} else if x > z {
return z
} else {
return y
}
Hands-on exercises
good notes!
Inside a function, the := short assignment statement can be used in place of a var declaration
with implicit type.
Outside a function, every statement begins with a keyword (var, func, and so on) and so the :=
construct is not available.
https://go.dev/tour/basics/10
The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on
64-bit systems. When you need an integer value you should use int unless you have a
specific reason to use a sized or unsigned integer type.
https://go.dev/tour/basics/11
Constants are declared like variables, but with the const keyword.
Constants can be character, string, boolean, or numeric values.
Constants cannot be declared using the := syntax.
https://go.dev/tour/basics/15
Hands-on exercise #10 (was #05) - zero value, :=, type specificity,
blank identifier
write a program that uses the following:
● var for zero value
● short declaration operator
● multiple initializations
● var when specificity is required
● blank identifier
print items as necessary to make the program interesting
https://go.dev/play/p/fr9O2i6Hvvg
curriculum item # 025-hands-on-exercise-10
video: 025-hands-on-exercise-10.mp4 was 025-hands-on-exercise-05.mp4
Introduction
● good stuff coming!
○ code preview of scope
● We are riding a rocket ship of a mystery!
○ Earth is traveling through space at 67,000 mph
○ Earth is spinning at 1,000 mph
○ Earth is circling a ball of fire in the sky (the sun)
■ the sun is 1.3 million times larger than earth
○ wow!
curriculum item # 029-intro-programming-fundamentals
video: 029-intro-programming-fundamentals.mp4
● declare
○ declare a variable to hold a VALUE of a certain TYPE
var x int
● assign
○ assign a value to a variable
var x int = 42
● initialize
○ declare & assign
○ assign an initial value to a variable
var x int = 42
var y int
● identifier
○ the name of a variable
○ examples: x, fName
● keywords
○ these are words that a reserved for use by the Go programming language
■ they are sometimes called “reserved words”
■ you can’t use a keyword for anything other than its purpose
■ https://go.dev/ref/spec#Keywords
● operator
○ in “2 + 2” the “+” is the OPERATOR
○ an operator is a character that represents an action, as for example “+” is an
arithmetic OPERATOR that represents addition
● operand
○ in “2 + 2” the “2”s are OPERANDS
● statement
○ the smallest standalone element of a program that expresses some action to be
carried out. It is an instruction that commands the computer to perform a
specified action.
○ A program is formed by a sequence of one or more statements.
x := 2+7
● expression
○ a value, or operations (operands and operators) that express a value
○ a combination of one or more explicit values, constants, variables, operators, and
functions that the programming language interprets and computes to produce
another value. For example, 2+7 is an expression which evaluates to 9. For
example, 42 is an expression as it's a value.
x := 2+7
y := 42
● parens
()
● curly braces “curlies”
[]
● scope
○ where a variable exists and is accessible
○ best practice: keep scope as “narrow” as possible
curriculum item # 030-terminology
Understanding scope
● exploring scope
● https://go.dev/ref/spec#Declarations_and_scope
https://go.dev/play/p/0sU5YFvI1bR
curriculum item # 031-scope
○ cd
○ cd ..
○ mkdir
○ touch
■ touch temp.txt
○ nano temp.txt
○ cat temp.txt
○ clear
■ command + k
○ chmod
■ chmod options permissions filename
■ chmod 777 temp.txt
○ env
■ go version
■ go env
○ rm <file or folder name>
■ rm -rf <file or folder name>
○ see info on a file
■ file <filename>
Todd McLeod - Learn To Code Go - Page 27
○ LOVE THESE
■ ulimit -a
■ ps aux | wc -l
● all of the processes running on your machine
■ lscpu
● only on linux/mac
● info about your hardware
■
○ all commands
curriculum item # 035-terminal-commands-pt-2
Checksums
A checksum in programming is a technique used to ensure the integrity of data that is being
transmitted or stored. It is a mathematical algorithm that calculates a fixed-size value based on
the content of a file or data stream. This value is then compared to the expected value to ensure
that the data has not been altered or corrupted during transmission or storage.
Checksums are commonly used in network protocols such as TCP/IP, as well as in file transfer
protocols such as FTP and HTTP. They are also used in computer storage systems, such as
hard drives and solid-state drives, to detect errors in data storage.
The most commonly used checksum algorithms include MD5, SHA-1, SHA-256, and CRC
(Cyclic Redundancy Check). Each algorithm produces a unique checksum value based on the
input data, which can be used to verify the integrity of the data.
It compiles the Go package(s) in the current directory and its subdirectories, if any.
It links the compiled object files into a single binary executable file.
It installs the binary executable file in the bin directory of your Go workspace, which is usually
located at $GOPATH/bin.
After executing go install, you can run the resulting binary executable file directly from the
command line, assuming the $GOPATH/bin directory is included in your system's PATH
environment variable.
curriculum item # 041-go-install
The go mod tidy command checks the go.mod file in your project, and it ensures that it includes
all the necessary dependencies for the project to build and run correctly. It also removes any
unused dependencies that are no longer required by the project.
In more detail, when you run the go mod tidy command, it performs the following steps:
● It reads the go.mod file and identifies all the dependencies listed in it.
● It checks if all the dependencies listed in the go.mod file are required for the project. If
any of the dependencies are not required, it removes them from the go.mod file.
● It adds any missing dependencies to the go.mod file that are required by the project.
● It verifies that all the dependencies have the correct versions specified in the go.mod file.
● It updates the go.sum file to include the hashes of the downloaded dependencies.
Overall, the go mod tidy command helps to ensure that your Go project has the correct
dependencies specified, and it removes any unused dependencies that may slow down your
project or cause compatibility issues.
curriculum item # 044-go-mod-in-action
The go mod tidy command checks the go.mod file in your project, and it ensures that it includes
all the necessary dependencies for the project to build and run correctly. It also removes any
unused dependencies that are no longer required by the project.
In more detail, when you run the go mod tidy command, it performs the following steps:
● It reads the go.mod file and identifies all the dependencies listed in it.
● It checks if all the dependencies listed in the go.mod file are required for the project. If
any of the dependencies are not required, it removes them from the go.mod file.
● It adds any missing dependencies to the go.mod file that are required by the project.
● It verifies that all the dependencies have the correct versions specified in the go.mod file.
● It updates the go.sum file to include the hashes of the downloaded dependencies.
In Go, modular code refers to breaking up a program into smaller, reusable modules that can be
easily managed and maintained. This approach helps developers write more organized and
maintainable code by focusing on building independent modules that perform a specific task or
have a specific functionality.
Dependency management is the process of handling external libraries and packages that a Go
program relies on. When developing a Go program, developers often use third-party libraries to
speed up development or add specific features. Dependency management refers to managing
these external libraries to ensure that the program runs as expected, and updates to the
libraries don't break the program's functionality.
The Go programming language has a built-in package manager called "go modules." The
module system provides a way to manage dependencies at the module level, allowing
developers to specify the exact version of a dependency that their program requires. The
module system also provides a way to easily download and manage dependencies for a Go
project, making it easy to build modular and maintainable code in Go.
https://go.dev/play/p/QJJ_2nWvKLI
curriculum item # 047-mod-code-depend-02
● Make sure you're in the branch you want to tag. You can use the command git branch to
see which branch you're currently in and git checkout <branch> to switch to another
branch if necessary.
● Decide on a version number for your tag. This can be any alphanumeric string, but it's
common to use a format like "v1.0" or "v1.1.2".
With these steps, you can easily create tags for specific versions of your codebase, making it
easier to track changes and collaborate with others.
Hands-on exercises
Housekeeping
Previewing code
A preview of this section's code
https://go.dev/play/p/-jMpY4wg264
curriculum item # 062-previewing-code
The Go runtime system is a component of the Go programming language that manages and
schedules the execution of Go programs. It includes a number of features that make it easy to
write concurrent and parallel programs in Go.
● Stack management: Each goroutine has its own stack, which is used to store local
variables and function arguments. The runtime system manages the allocation and
deallocation of stack space for each goroutine.
Overall, the Go runtime system is a key component of the Go programming language that
makes it easy to write concurrent and parallel programs. It provides a number of powerful
features that simplify the process of managing threads, memory, and communication between
concurrent processes.
The stack is a region of memory used for storing variables that are local to a function or a
goroutine. When a function is called or a goroutine is created, a new stack frame is created on
the stack to store the function arguments, local variables, and other data. The stack is a LIFO
(last-in-first-out) data structure, which means that the most recently added data is the first to
be removed. The stack is generally faster than the heap because it is managed automatically
by the Go runtime system, and memory allocation and deallocation is relatively fast.
The heap is a region of memory used for storing variables that have a longer lifetime than
those stored on the stack. When a variable is allocated on the heap, it remains there until it is
explicitly deallocated by the program or until the program exits. The heap is a more flexible
data structure than the stack, but it is generally slower because it requires more overhead for
memory allocation and deallocation. In Go, the heap is managed automatically by the
garbage collector, which frees up memory that is no longer being used by the program.
In general, Go programs try to allocate as much memory as possible on the stack because it
is faster and requires less overhead. However, when a program needs to store large amounts
of data or data with a longer lifetime than the stack, it must use the heap. The Go runtime
system provides automatic memory management for both the stack and the heap, making it
easy to write efficient and scalable concurrent programs.
The Go compiler is a program that translates Go source code into machine-readable binary
code that can be executed by a computer. In other words, the Go compiler takes the
human-readable code that a programmer writes in Go and converts it into instructions that a
computer can understand and execute.
● Type checking: The compiler checks that the types of variables and expressions in the
code are consistent and correct according to the rules of the Go language.
● Optimization: The compiler performs various optimizations to the code to make it run
more efficiently on the target machine.
● Code generation: The compiler generates machine-readable binary code that can be
executed by the target machine.
In Go, the standard compiler is called "gc" (short for "Go Compiler"). It is a highly optimized
and efficient compiler that generates fast and efficient code.
Program execution
A complete program is created by linking a single, unimported package called the main
package with all the packages it imports, transitively. The main package must have package
name main and declare a function main that takes no arguments and returns no value.
func main() { … }
Program execution begins by initializing the main package and then invoking the function
main. When that function invocation returns, the program exits. It does not wait for other
(non-main) goroutines to complete.
https://go.dev/ref/spec#Program_execution
https://go.dev/play/p/-jMpY4wg264
curriculum item # 063-understanding-control-flow
Comparison operators compare two operands and yield an untyped boolean value.
== equal
For example, in the expression 11 % 3, the modulus operator would return 2, because 11
divided by 3 leaves a remainder of 2.
The modulus operator is commonly used in programming for tasks such as checking if a number
is even or odd, or for calculating the position of elements in an array.
https://go.dev/play/p/OeOR10EATed
curriculum item # 072-modulus
Hands-on exercises
😃🌴🌞🔆🌻🙌🌴🌴🌴🌴🌴🌴🌴🌴
● use a for range loop to print each value and the index position of each value
Additional code
curriculum item # 090-additional-code
"Almond Biscotti Café", "Banana Pudding ", "Balsamic Strawberry (GF)", "Bittersweet
Chocolate (GF)", "Blueberry Pancake (GF)", "Bourbon Turtle (GF)", "Browned Butter
Cookie Dough", "Chocolate Covered Black Cherry (GF)", "Chocolate Fudge Brownie",
"Chocolate Peanut Butter (GF)", "Coffee (GF)", "Cookies & Cream", "Fresh Basil (GF)",
"Garden Mint Chip (GF)", "Lavender Lemon Honey (GF)", "Lemon Bar", "Madagascar
Vanilla (GF)", "Matcha (GF)", "Midnight Chocolate Sorbet (GF, V)", "Non-Dairy Chocolate
Peanut Butter (GF, V)", "Non-Dairy Coconut Matcha (GF, V)", "Non-Dairy Sunbutter
Cinnamon (GF, V)", "Orange Cream (GF) ", "Peanut Butter Cookie Dough", "Raspberry
Sorbet (GF, V)", "Salty Caramel (GF)", "Slate Slate Different", "Strawberry Lemonade
Sorbet (GF, V)", "Vanilla Caramel Blondie", "Vietnamese Cinnamon (GF)", "Wolverine
Tracks (GF)"
https://go.dev/play/p/OEHPUGDoWD_t
curriculum item # 094-hands-on-exercise-40
"Almond Biscotti Café", "Banana Pudding ", "Balsamic Strawberry (GF)", "Bittersweet
Chocolate (GF)", "Blueberry Pancake (GF)", "Bourbon Turtle (GF)", "Browned Butter
Cookie Dough", "Chocolate Covered Black Cherry (GF)", "Chocolate Fudge Brownie",
"Chocolate Peanut Butter (GF)", "Coffee (GF)", "Cookies & Cream", "Fresh Basil (GF)",
"Garden Mint Chip (GF)", "Lavender Lemon Honey (GF)", "Lemon Bar", "Madagascar
Vanilla (GF)", "Matcha (GF)", "Midnight Chocolate Sorbet (GF, V)", "Non-Dairy Chocolate
Peanut Butter (GF, V)", "Non-Dairy Coconut Matcha (GF, V)", "Non-Dairy Sunbutter
Cinnamon (GF, V)", "Orange Cream (GF) ", "Peanut Butter Cookie Dough", "Raspberry
Sorbet (GF, V)", "Salty Caramel (GF)", "Slate Slate Different", "Strawberry Lemonade
Sorbet (GF, V)", "Vanilla Caramel Blondie", "Vietnamese Cinnamon (GF)", "Wolverine
Tracks (GF)"
https://go.dev/play/p/W8sWn7fsUXh
curriculum item # 096-hands-on-exercise-41
Slice - make
Slices are built on top of arrays. A slice is dynamic in that it will grow in size. The underlying
array, however, does not grow in size. When we create a slice, we can use the built in
function make to specify how large our slice should be and also how large the underlying
array should be. This can enhance performance a little bit.
○ make([]T, length, capacity)
○ make([]int, 50, 100)
code
● https://go.dev/play/p/CwfCyWF3Tei
● https://play.golang.org/p/07hH1b-hvD
curriculum item # 101-slice-make
Hands-on exercises
Range over the records, then range over the data in each record.
https://go.dev/play/p/2jxGMyXiZrE
curriculum item # 112-hands-on-exercise-48
Map - introduction
A map is a key-value store. This means that you store some value and you access that value by
a key. For instance, I might store a friend's name “Daniel” as a key and then have the value of a
phone number "777-888-9999" as the value. This way I could enter my friend’s name and have
the map return their phone number. The syntax for a map is map[key]value. The key can be of
Todd McLeod - Learn To Code Go - Page 60
any type which allows comparison (eg, I could use a string or an int, for example, as I can
compare if two strings are equal, or if two ints are equal). It is important to note that maps are
unordered. If you print out all of the keys and values in a map, they will print out in random
order. A map is the perfect data structure when you need to look up data fast.
● creating a map
● add an element to your map
● accessing elements in a map
● printing out the entire map
● seeing the length of the map
○ len
○ https://pkg.go.dev/builtin#len
https://go.dev/play/p/OmgfgoUxsgV
curriculum item # 113-map-intro
key value
https://go.dev/play/p/nAqHIjP8JTZ
curriculum item # 118-hands-on-exercise-49
key value
https://go.dev/play/p/3quGhpVWGg2
curriculum item # 121-hands-on-exercise-52
Struct introduction
A struct is an composite data type. (composite data types, aka, aggregate data types, aka,
complex data types). Structs allow us to compose together values of different types.
https://go.dev/play/p/3NnHshCydm5
curriculum item # 122-intro-to-structs
Embedded structs
We can take one struct and embed it in another struct. When you do this the inner type gets
promoted to the outer type.
https://go.dev/play/p/w8WBKiKhGX4
curriculum item # 123-embedded-structs
Anonymous structs
We can create anonymous structs also. An anonymous struct is a struct which is not
associated with a specific identifier.
https://go.dev/play/p/o-csXhWFnFH
curriculum item # 124-anonymous-structs
Composition
composite data types, aka, aggregate data types, aka, complex data types
In Go, composition refers to a way of structuring and building complex types by combining
multiple simpler types. Composition is one of the fundamental principles of object-oriented
programming and allows you to create more flexible and reusable code.
One of the ways composition is achieved is by embedding one struct type within another.
The fields and methods of the embedded "inner" struct become accessible to the outer struct.
The inner type is said to be promoted to the outer type. In addition, methods of the inner type
are also promoted to the outer type. This concept is similar to inheritance in traditional
object-oriented languages, but Go does not have a built-in inheritance mechanism.
// engine method
func (e *Engine) Start() {
fmt.Println("Engine started")
}
func main() {
car := Car{}
car.Start() // Calling the Start() method from the embedded Engine struct
}
In the example above, we have two struct types: `Engine` and `Car`. The `Car` struct embeds
the `Engine` struct, which means it includes all the fields and methods of the `Engine` struct
within itself. This allows the `Car` struct to access and use the `Start()` method of the embedded
`Engine` struct.
By using composition, you can build complex types by combining simpler ones, promoting
code reuse and modular design. It provides a way to create relationships between different
types without the need for traditional inheritance.
Introduction to functions
● introduction to modular code
○ spaghetti code
○ structured / procedural programming
■ purpose
● "abstract" code
● code reusability
● more understandable
■ functions
■ packages
In Go programming language, a function is a group of statements that together perform a
specific task. It is an important concept as it allows us to create reusable and modular code.
Every Go program has at least one function, which is the `main()` function. The execution of a
Go program starts and ends with this function.
```go
func functionname(parametername type) returntype {
//function body
}
```
3. `parametername type`: These are the parameters accepted by the function. You can provide
any number of parameters separated by a comma. Each parameter has a name and a type.
4. `returntype`: This is the data type of the value the function returns. If a function doesn't return
any value, the return type can be omitted.
5. `{ function body }`: The function body is enclosed between `{}`. The function body can contain
statements and also a return statement to return a value from the function.
**For example:**
```go
func add(x int, y int) int {
return x + y
}
func main() {
result := add(10, 20)
fmt.Println(result)
}
```
In this example, we've defined a function named `add` that takes two integer parameters and
returns an integer. It adds the two numbers and returns the result. In the `main()` function, we
call the `add` function with two numbers, and then print the result.
```go
func rectangle(length, width int) (int, int) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter
}
func main() {
area, perimeter := rectangle(10, 20)
fmt.Println("Area: ", area)
fmt.Println("Perimeter: ", perimeter)
}
In this example, the `rectangle` function calculates the area and perimeter of a rectangle, and
returns them. In the `main()` function, we call the `rectangle` function and print the returned area
and perimeter.
There's a lot more to functions in Go, like variadic functions, function literals (also known as
anonymous functions), higher-order functions, and more. However, this should give you a good
starting point for understanding functions in Go.
Syntax of functions in Go
● func (receiver) identifier(parameters) (returns) { code }
○ parameters & arguments
■ we define our func with parameters (if any)
■ we call our func and pass in arguments (in any)
● Everything in Go is PASS BY VALUE
● sample funcs
○ no params, no returns
○ 1 param, no returns
○ 1 param, 1 return
○ 2 params, 2 returns
https://go.dev/play/p/VXAaDKtgVAq
curriculum item # 131-func-syntax
Variadic parameter
You can create a func which takes an unlimited number of arguments. When you do this,
this is known as a “variadic parameter.” When use the lexical element operator “...T” to signify a
variadic parameter (there “T” represents some type).
● create a func that takes an unlimited number of VALUES of TYPE int
A variadic parameter in Go programming language is a parameter that can take an arbitrary
number of arguments of a particular type. It's a way of allowing a function to accept as many
arguments as needed.
You declare a variadic function with an ellipsis `...` before the type of the last parameter. Here's
an example:
```go
func sum(nums ...int) int {
total := 0
In the `sum` function, `nums` is a variadic parameter of type `int`. The function can be called
with any number of `int` arguments:
```go
sum(1, 2)
sum(1, 2, 3)
sum(1, 2, 3, 4)
```
Inside the function, `nums` is an `[]int` (slice of `int`). The `range` keyword is used to iterate over
this slice.
https://go.dev/play/p/N_VWQgL0u0o
curriculum item # 132-variadic-parameter
Unfurling a slice
When you have a slice of some type, you can pass in the individual values in a slice by
using the “...” operator.
When you pass a slice of type T into a function that has a variadic parameter of type T in
Go, you're essentially passing a variable number of arguments of the same type to the
function. In Go, this feature is called "variadic functions".
However, it's important to note that you can't directly pass a slice to a variadic function.
You need to use the ellipsis (...) operator to unpack the slice, as shown in this example:
```go
func Sum(nums ...int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
func main() {
In the example above, `nums...` is the use of the ellipsis operator to unpack the `nums`
slice into a number of arguments to match the `nums ...int` parameter in the `Sum`
function. This is the mechanism used to pass a slice to a variadic function in Go.
https://go.dev/play/p/10tDA0_aswX
curriculum item # 133-unfurling-a-slice
Defer
●
A "defer" statement invokes a function whose execution is deferred to the moment
the surrounding function returns, either because
○ the surrounding function executed a return statement,
○ reached the end of its function body,
○ or because the corresponding goroutine is panicking.
In Go programming language, the `defer` keyword is used to ensure that a function call is
executed later in a program's execution, usually for purposes of cleanup. `defer` is often used
where `ensure` and `finally` would be used in other languages.
When a function call is deferred, it's placed onto a stack. The function calls on the stack are
executed when the surrounding function returns, not when the `defer` statement is called.
They're executed in Last In, First Out (LIFO) order, so if you have multiple `defer` statements,
the most recently deferred function will be the first one to execute when the function returns.
```go
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
```
Despite the "world" line being deferred before "hello" is printed, it only actually gets printed after
the `main` function has finished executing all its other statements, including printing "hello".
This makes `defer` particularly useful for handling things like closing files or releasing resources.
For instance, you can open a file, defer the `Close()` operation, and then proceed with reading
from or writing to the file, confident in the knowledge that it will be closed properly when the
function is done, no matter what happens:
```go
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("myfile.txt")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
In the above code, even if an error occurs while working with the file, the `defer f.Close()`
statement ensures that the file will be closed before the function returns.
https://go.dev/play/p/x_abhRmKDdx
curriculum item # 134-defer
```go
func (receiver ReceiverType) MethodName(parameters) ReturnType {
// method body
}
```
In this syntax:
Here is an example of a method in Go. Assume we have a struct type `Circle`, with a method
`Area`:
```go
type Circle struct {
Radius float64
}
In this example, `Area` is a method with receiver type `Circle`. You can call it like this:
In this case, `c` is an instance of type `Circle`, and `Area` is a method that can be called on
instances of `Circle`.
Note: If a method needs to modify the receiver or the receiver is large, it's common to use a
pointer to the receiver type instead:
```go
func (c *Circle) Scale(factor float64) {
c.Radius *= factor
}
```
In this case, `Scale` is a method with receiver type `*Circle` (pointer to Circle). It modifies the
`Radius` of the circle it is called on. The method can be called as follows:
```go
c := &Circle{Radius: 5}
c.Scale(2)
fmt.Println(c.Radius) // Output: 10
```
This shows that the `Radius` of `c` was indeed modified by the `Scale` method.
https://go.dev/play/p/1bCH57BFVpD
curriculum item # 135-methods
Now, you can have TYPES "Dog", "Cat", and "Bird" define their own implementation of the
method "MakeSound()" and any VALUES of TYPE "Dog", "Cat", and "Bird" will also be of
TYPE "Animal"
When you call the "MakeSound()" method for any VALUE of TYPE "Animal", the specific
implementation for the underlying TYPE ("Dog", "Cat", or "Bird") will be invoked. This behavior
is known as polymorphism.
In simpler terms, polymorphism allows you to treat different VALUES of different TYPES as
the same interface TYPE when they share a common interface. This enables you to write
code that can operate on a variety of TYPES without needing to know their exact underlying
TYPE, as long as they adhere to the shared behavior defined by the interface TYPE.
We create an interface using this syntax: “keyword identifier type” so for an interface it would
be: “type human interface” We then define which method(s) a type must have to implement that
interface.
If a TYPE has the required methods, which could be none (the empty interface denoted by
interface{} or any), then that TYPE implicitly implements the interface and is also of that
interface type.
1. Interfaces:
An interface in Go defines a set of method signatures. It describes the behavior or contract
that a type should adhere to. Any type that implements the methods specified in an interface is
said to satisfy that interface. In Go, interfaces are implicitly implemented; there's no need to
explicitly declare that a type implements an interface.
The `Shape` interface has two methods: `Area()` and `Perimeter()`, both of which return
`float64`. Any type that defines these two methods will implicitly satisfy the `Shape` interface.
For instance, if we have a `Circle` type with `Area()` and `Perimeter()` methods, it satisfies the
Now, if we create a variable of the `Shape` type and assign a `Circle` instance to it, it is valid
because `Circle` implements the `Shape` interface.
Interfaces enable polymorphism in Go, allowing us to work with different types that
satisfy a common interface without knowing the exact underlying type.
2. Polymorphism:
Polymorphism is the ability of an object to take on different forms or types. In Go,
polymorphism is achieved through interfaces. Since a type can satisfy multiple interfaces, we
can use interfaces to define different behaviors for a single type.
In this example, we have two types, `Dog` and `Cat`, both implementing the `Say()` method.
The `LetThemSpeak()` function takes a parameter of type `Sayer`, which is an interface. When
we pass a `Dog` or `Cat` instance to this function, it calls the `Say()` method specific to that
instance. This behavior demonstrates polymorphism, as the function can work with different
types that implement the `Sayer` interface.
dog := Dog{}
cat := Cat{}
Go interfaces define sets of method signatures, and types implicitly satisfy these interfaces.
Polymorphism in Go is achieved through interfaces, allowing different types to be treated
uniformly based on their shared interface implementation. This flexibility promotes code
reusability and abstraction.
https://go.dev/play/p/_wYpgHEzkvU
curriculum item # 136-interfaces-polymorph
To implement the `Stringer` interface, a type simply needs to define the `String()` method with
the following signature:
Here, `T` represents the type implementing the `Stringer` interface. Inside the `String()` method,
you should generate and return the desired string representation of the object of type `T`.
Once a type implements the `Stringer` interface, it can be used with the `fmt` package to format
and print the object using the `Print` and `Printf` functions or in string interpolation. When a
package main
import (
"fmt"
)
func main() {
p := Person{Name: "John Doe", Age: 30, Gender: "Male"}
fmt.Println(p) // Automatically calls the String() method of Person
}
In this example, the `Person` struct implements the `Stringer` interface by defining the `String()`
method. The `String()` method returns a formatted string representation of the `Person` object.
When `fmt.Println(p)` is called, it automatically invokes the `String()` method to obtain the string
representation, resulting in the output: "Name: John Doe, Age: 30, Gender: Male".
https://go.dev/play/p/L4TkCiyYn1e
curriculum item # 137-interfaces-polymorph-stringer
1. Logging: A wrapper function can add logging statements before and after invoking the
wrapped function. This helps in capturing information about the function calls, input parameters,
return values, and any errors that may occur.
2. Timing and profiling: Wrappers can be used to measure the execution time of functions,
enabling performance analysis and profiling. By recording the start and end times, you can
calculate the elapsed time and gather statistics.
4. Error handling: Wrappers can intercept errors returned by the wrapped function and
transform them into a different error type or add more contextual information. They can also
recover from panics and gracefully handle exceptional situations.
package main
import (
"fmt"
"time"
)
// Function to be wrapped
func MyFunction() {
time.Sleep(2 * time.Second) // Simulate some work
fmt.Println("MyFunction completed")
}
func main() {
In the example above, the `TimedFunction` acts as a wrapper function that measures the
elapsed time taken by `MyFunction` to execute. It captures the start time, calls `MyFunction`,
calculates the elapsed time, and then prints it. By using the wrapper, you can easily add timing
functionality to multiple functions without modifying their implementation.
Please note that the specific implementation of wrapper functions can vary depending on the
use case and requirements. Wrappers can be written as higher-order functions that accept
function arguments, or they can be implemented as methods of custom types. The choice of
implementation depends on the specific needs of your application.
https://go.dev/play/p/oDvKjKxC4A6
curriculum item # 138-interfaces-stringer-wrapper-func
The `Write` method takes a byte slice (`[]byte`) as input and returns the number of bytes
written and an error (if any). It writes the contents of the byte slice to the underlying output
destination.
To write to a file using the writer interface, you need to create a file or open an existing file using
the `os.Create` or `os.OpenFile` function, respectively. These functions return a file that
implements the `io.Writer` interface. Here's an example:
package main
import (
"io"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
panic(err)
_, err = file.Write(data)
if err != nil {
panic(err)
}
}
In this example, we use `os.Create` to create a new file called "output.txt" in the current
directory. The `Create` function returns a file (`*os.File`) that satisfies the `io.Writer` interface.
We then defer the closing of the file using `file.Close()` to ensure it gets closed properly.
Next, we create a byte slice `data` containing the content we want to write to the file. We call
`file.Write(data)` to write the byte slice to the file. The `Write` method returns the number of
bytes written and any error encountered during the write operation. If there's an error, we panic
and terminate the program.
By utilizing the writer interface, you can write data to files in Go using a consistent and flexible
approach. Additionally, this interface is widely used in the Go standard library, enabling
compatibility with various output destinations beyond files, such as network connections or other
data streams.
In Go, a string and a []byte are two different types, but they are closely related and can often
be converted between each other.
On the other hand, a []byte is a slice of bytes, where each element represents a single byte.
It is a mutable type, so you can modify individual bytes within a byte slice. It can be used to
represent binary data or text in various encodings.
1. Converting a string to a byte slice: You can convert a string to a byte slice using the
`[]byte()` type conversion. For example:
str := "Hello"
This will create a new byte slice `bytes` that contains the UTF-8 encoded representation of
the string "Hello". Modifying `bytes` will not affect the original string.
2. Converting a byte slice to a string: You can convert a byte slice to a string using the
`string()` type conversion. For example:
This will create a new string `str` that represents the UTF-8 encoded text corresponding to the
byte slice. Modifying `str` will not affect the original byte slice.
It's important to note that converting between strings and byte slices involves encoding and
decoding operations, which can introduce potential errors if the encoding is not handled
correctly. It's essential to be mindful of the encoding when working with these conversions to
ensure accurate representation of the data.
Overall, strings and byte slices provide different ways of representing and manipulating textual
or binary data in Go, and Go provides convenient methods to convert between these two
types when necessary.
The concept of a byte buffer is not specific to any particular programming language but is a
general concept in computer programming. Different programming languages may provide
their own implementations of byte buffers, each with its own set of features and functionality.
The purpose of a byte buffer is to provide a flexible and efficient way to work with
sequences of bytes. It typically offers methods or functions for operations such as appending
data, reading data, resizing the buffer, and converting the buffer's contents to different types
(e.g., strings, integers, etc.).
A byte buffer is a data structure that provides a convenient interface to manipulate sequences
of bytes efficiently. It serves as a temporary storage for byte data and enables operations
such as reading, writing, appending, and resizing byte sequences.
You can create a new `bytes.Buffer` by using the `NewBuffer` function provided by the `bytes`
package:
import "bytes"
The `nil` argument passed to `NewBuffer` initializes the buffer with an empty byte slice.
Alternatively, you can initialize the buffer with an existing byte slice by passing it to
`NewBuffer`, like this:
Once you have a `bytes.Buffer`, you can perform various operations on it. Some common
operations include:
func main() {
buffer := bytes.NewBufferString("Hello, ")
In this example, we create a `bytes.Buffer` initialized with the string "Hello, ". We then append
"World!" to the buffer, read the contents, reset the buffer, and append new data. Finally, we
read the contents again to see the updated value.
`bytes.Buffer` is a flexible and efficient way to work with byte sequences in Go, often used for
tasks like string manipulation, file I/O, or network communication.
https://go.dev/play/p/PqWPMd5X2Om
curriculum item # 140-writer-interface-byte-buffer
import (
"bytes"
"fmt"
"io"
"os"
)
func main() {
// Writing to a file
f, err := os.Create("test.txt")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
writeString(f, "Hello, File!\n")
// Writing to a Buffer
var b bytes.Buffer
writeString(&b, "Hello, Buffer!\n")
Anonymous func
Anonymous self-executing func
● no parameters
● 1 parameter
An anonymous function, often also called a function literal, lambda, or closure, is a
function that is defined without being given a name. Anonymous functions can be used in
programming where a function is expected but a named function is not necessary or not
desirable.
In Go, the concept of anonymous functions is supported. They can be used wherever function
types are expected.
```go
func() {
fmt.Println("I'm an anonymous function!")
}()
```
In this example, the function is declared using the `func` keyword, it takes no arguments, and it
doesn't return any value. The function's body is enclosed in `{}` and then the function is
immediately invoked with `()`.
You can also assign an anonymous function to a variable and then invoke it using the variable
name. Here's an example:
```go
printFunction := func() {
fmt.Println("I'm an anonymous function assigned to a variable!")
}
```go
multiply := func(a int, b int) int {
return a * b
}
It's important to note that, since Go supports closures, an anonymous function can access and
modify variables defined outside of the function itself. This is a powerful feature, but it can also
lead to unexpected side effects if not used carefully.
https://go.dev/play/p/p0-fXb-Ugvt
anonymous function with a return: https://go.dev/play/p/_v__udo_i3v
curriculum item # 142-anonymous-func
func expression
Assigning a func to a variable
● assign a function to a variable
○ execute it
● an expression is something that evaluates to a value
● "first class citizen"
An expression is a combination of values, variables, operators, and function calls that are
evaluated to produce a single value. It can be as simple as a literal value or a variable, or it
can involve complex operations and function invocations.
1. Literal values:
- `42`: An integer literal expression representing the value 42.
- `"Hello, World!"`: A string literal expression representing the text "Hello, World!".
2. Variables:
- `x`: A variable expression representing the value stored in the variable `x`.
3. Arithmetic expressions:
- `x + y`: An arithmetic expression that adds the values of variables `x` and `y`.
4. Function calls:
- `math.Sqrt(16)`: A function call expression invoking the `Sqrt` function from the `math`
package with the argument `16`.
- `fmt.Sprintf("Value: %d", x)`: A function call expression invoking the `Sprintf` function from
the `fmt` package, formatting the string "Value: %d" with the value of the variable `x`.
5. Logical expressions:
- `x > 10 && y < 20`: A logical expression combining the greater-than and less-than operators
with logical AND.
6. Type conversions:
- `float64(x)`: A type conversion expression that converts the value of `x` to a `float64`.
Expressions are the building blocks of Go programs, and they are used to perform
computations, manipulate values, and make decisions based on conditions. They can be
assigned to variables, passed as arguments to functions, or used in control flow
statements like if statements and loops.
The term "first-class citizen" refers to the status of certain entities, such as values, types,
and functions, that are treated equally and have the same capabilities as other entities in
the language. It means that these entities can be assigned to variables, passed as
arguments to functions, and returned as values from functions, just like any other data type
in the language.
In Go, functions are considered first-class citizens. They can be assigned to variables,
passed as arguments to other functions, and returned as values from functions. This
allows Go to support higher-order functions, where functions can operate on other functions. For
example, you can define a function that takes another function as an argument and then call
that function within the body of the function.
package main
import "fmt"
func main() {
result1 := applyOperation(5, 3, add) // Passing 'add' function as argument
result2 := applyOperation(8, 4, subtract) // Passing 'subtract' function as argument
fmt.Println(result1) // Output: 8
fmt.Println(result2) // Output: 4
}
In the example, the `applyOperation` function takes two integers and a function as arguments. It
applies the given function to the integers and returns the result. The `add` and `subtract`
functions are defined separately and passed as arguments to `applyOperation` when calling it.
The ability to treat functions as first-class citizens in Go allows for more flexible and modular
code, enabling the use of higher-order functions, function composition, and other functional
programming techniques.
https://go.dev/play/p/rd1eQMjlvYD
curriculum item # 143-func-expression
Returning a func
You can return a func from a func. Here is what that looks like.
Returning a function in the Go programming language is a form of using higher-order functions,
which are functions that can accept other functions as arguments and/or return other functions
as results. This is a common practice in functional programming paradigms, but it's also
available in multiparadigm languages like Go.
```go
package main
import "fmt"
func main() {
increment := incrementer()
fmt.Println(increment()) // Output: 1
fmt.Println(increment()) // Output: 2
fmt.Println(increment()) // Output: 3
}
```
In this code, the `incrementer` function creates a variable `i` and then defines an anonymous
function that increments `i` by one each time it's called. This anonymous function captures the
`i` variable from its enclosing scope, a feature known as closure in computer science.
The `incrementer` function returns this anonymous function, and then in `main`, we call
`incrementer` and assign the returned function to the `increment` variable. This variable is now
itself a function that we can call, and each time we call it, it increments its internal `i` counter and
returns the new value.
https://go.dev/play/p/mfjfy63Pzii
curriculum item # 144-returning-a-func
Callback
● passing a func as an argument
The term "callback" in programming refers to a function or a piece of code that is passed
as an argument to another function. The term itself can be understood by breaking it down
into "call" and "back."
The "call" part refers to the action of invoking or executing a function. When a function is
called, it starts executing its code, performs certain operations, and then returns a result.
The "back" part refers to the idea that the callback function is called back or invoked by the
original function after it has completed its execution or reached a specific point in its code.
Callbacks are often used in event-driven programming or asynchronous operations, where the
flow of execution is not linear. For example, in web development, callbacks are commonly
used with JavaScript's asynchronous functions to handle responses from server requests or
user interactions.
The term "callback" has become a widely accepted convention in programming to describe
this mechanism of passing functions as arguments to other functions and invoking them later.
It emphasizes the idea that the callback function is "called back" by the original function,
enhancing the understanding of the code's flow and behavior.
package main
import "fmt"
func main() {
result1 := applyOperation(5, 3, add) // Passing 'add' function as argument
result2 := applyOperation(8, 4, subtract) // Passing 'subtract' function as argument
fmt.Println(result1) // Output: 8
fmt.Println(result2) // Output: 4
}
https://go.dev/play/p/7UJCNM0UTWP
https://go.dev/play/p/cm1CRE4o59z
curriculum item # 145-callback-func-as-argument
In programming, closure refers to the combination of a function (or a method) and the
environment in which it was defined. It allows a function to access variables and bindings that
are outside of its own scope, even after the outer function has finished executing.
A closure is created when a nested function references a variable from its containing (parent)
function. The nested function retains a reference to the environment in which it was defined,
so it can access and manipulate variables from that environment, even if the parent function
has already completed.
Closures are particularly useful in situations where you want to create functions that have
access to certain variables or data that persist across multiple invocations. They enable you
to encapsulate data and behavior together, creating self-contained functions with their own
private state.
https://go.dev/play/p/207g7uGdgUF
curriculum item # 146-closure
Function fundamentals
This is a review of the fundamentals of functions in the Go programming language.
https://go.dev/play/p/S4INA-7i9_v
curriculum item # 147-function-fundamentals
Recursion
● a func that calls itself
● factorial example
When a recursive function is called, it solves a smaller instance of the same problem, and
then combines the result of the smaller instance with the current instance to obtain the final
Recursion can be a powerful technique for solving problems that exhibit a recursive structure,
such as tree traversal, graph traversal, and many mathematical calculations. However, it's
important to design recursive functions carefully, ensuring that they have well-defined base
cases and properly handle the termination condition, to avoid infinite recursion.
https://go.dev/play/p/ORGm7AQ3ljU
curriculum item # 148-recursion
Wrapper function
In the Go programming language, a wrapper function, also known as a wrapper, is a function
that provides an additional layer of abstraction or functionality around an existing function or
method. It acts as an intermediary between the caller and the wrapped function, allowing you
to modify inputs, outputs, or behavior without directly modifying the original function. A
wrapper function wraps or modifies another function's behavior.
Wrapper functions are commonly used for various purposes, such as:
1. Logging: A wrapper function can add logging statements before and after invoking the
wrapped function. This helps in capturing information about the function calls, input parameters,
return values, and any errors that may occur.
2. Timing and profiling: Wrappers can be used to measure the execution time of functions,
enabling performance analysis and profiling. By recording the start and end times, you can
calculate the elapsed time and gather statistics.
4. Error handling: Wrappers can intercept errors returned by the wrapped function and
transform them into a different error type or add more contextual information. They can also
recover from panics and gracefully handle exceptional situations.
```go
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
data, err := readFile("test.txt")
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
fmt.Printf("Data: %s\n", data)
}
```
In this example, the `readFile` function is a wrapper around the `ioutil.ReadFile` function. If
`ioutil.ReadFile` returns an error, the `readFile` function wraps this error with additional context
(the name of the file that caused the error) and returns this new error.
When the `readFile` function is called from `main`, the error handling logic can use the
additional context provided by the `readFile` function to provide more detailed error messages.
This approach helps to create more robust and maintainable code by centralizing error handling
logic within the wrapper function.
package main
// Function to be wrapped
func MyFunction() {
time.Sleep(2 * time.Second) // Simulate some work
fmt.Println("MyFunction completed")
}
func main() {
// Call the wrapped function
TimedFunction(MyFunction)
}
In the example above, the `TimedFunction` acts as a wrapper function that measures the
elapsed time taken by `MyFunction` to execute. It captures the start time, calls `MyFunction`,
calculates the elapsed time, and then prints it. By using the wrapper, you can easily add timing
functionality to multiple functions without modifying their implementation.
Please note that the specific implementation of wrapper functions can vary depending on the
use case and requirements. Wrappers can be written as higher-order functions that accept
function arguments, or they can be implemented as methods of custom types. The choice of
implementation depends on the specific needs of your application.
https://go.dev/play/p/scIeQnXpF8-
curriculum item # 149-wrapper-func
Hands-on exercises
Go provides a built-in testing framework that simplifies the process of testing Go code. Here is
an overview of the file structure, naming conventions, and how testing works in Go:
1. Test files: Test files live in the same package as the code they test. They are named with
the following convention: `filename_test.go` where filename is the name of the file that
contains the code you want to test.
2. Test functions: Test functions must start with the word `Test` followed by a word that starts
with a capital letter. The signature of a test function is `func TestXxx(*testing.T)`, where Xxx
does not start with a lowercase letter.
Example:
```go
// main.go
package main
To test this function, you would create another file in the same directory called `main_test.go`.
```go
// main_test.go
package main
import "testing"
In the test function, we used the `Add` function and provided it with two integers. We then
compared the result with the expected value. If the result is not what we expect, we report an
error using `t.Errorf`.
To run tests in Go, open your terminal and navigate to the directory containing your files, then
type:
```shell
go test
```
If the test fails, the `go test` command will report an error and provide the output from the
`t.Errorf` function.
Go's testing package also provides more advanced features like benchmarks, test helpers,
and skipping tests. To explore more advanced usage, you can check the official Go testing
documentation.
https://go.dev/play/p/GhR7rzvxU3_p
Todd McLeod - Learn To Code Go - Page 101
curriculum item # 156-hands-on-63-tests-01
In Go programming language, a unit test usually tests a single function, method, or struct. The
goal is to confirm that the behavior is correct.
Unit tests in Go are typically written using the built-in testing package, `testing`. This
package does not require any third-party libraries, but it's somewhat limited compared to
some other languages' testing tools. Despite its simplicity, it has enough features to construct
effective unit tests.
When you ask if this differs from a "test" in Go, it's worth clarifying that a "unit test" is a subset
of "test". Tests in software can take many forms such as unit tests, integration tests,
functional tests, system tests, etc.
An "Integration Test", for example, in contrast to a "Unit Test", would test the interaction
between multiple components of the system, to ensure they work together correctly.
So, to summarize, in Go, a unit test is just one kind of test you can conduct, focused on
verifying the correct behavior of a small, isolated piece of functionality. Other types of tests
have different focuses and may require different tools or techniques.
package main
import "fmt"
func main() {
fmt.Printf("%T\n", add)
fmt.Printf("%T\n", subtract)
fmt.Printf("%T\n", doMath)
https://go.dev/play/p/ysOWyO9N3Kj
curriculum item # 157-hands-on-64-tests-02-unit-tests
1. Package documentation: This should be a brief one-liner that begins with "Package
`<packagename>`" and provides a high-level overview of the package's purpose. It is typically
located in a separate `doc.go` file.
```go
// Add calculates the sum of two integers.
func Add(x, y int) int {
return x + y
}
```
3. Type documentation: Similar to functions, comments should be written directly above the
type definition, starting with the type's name.
```go
// Person represents a person with a name and age.
type Person struct {
Name string
Age int
}
```
4. Constant and variable documentation: Follows the same pattern, starting with the
constant or variable name.
```go
// Pi is the ratio of the circumference of a circle to its diameter.
const Pi = 3.14159265358979323846
```
- Use complete sentences. The first sentence should be a summary starting with the name
being declared.
- Prefer to break long lines after punctuation or operators, keeping operands together.
- Do not use URLs in package-level comments. It's better to create a README file for that
purpose.
Remember, the goal of comments is to improve code readability and provide context to other
developers. It's not about explaining what the code does (the code should speak for itself in
that regard), but rather why it does it.
The go doc command shows documentation for exported names (i.e., identifiers that start with
However, if you want to see documentation for unexported identifiers in your package while
you're developing, you can use go doc -u. This will show the documentation for both exported
and unexported identifiers. Note that these options are useful for package developers but are
less useful for consumers of the package since they cannot access unexported identifiers.
Please check the current Go documentation or help for go doc (go help doc) for the most
up-to-date information, as the behavior and features of Go tools may change.
https://go.dev/play/p/jxFzGsHyHfQ
curriculum item # 159-hands-on-66-documenting-code-with-comments
Callback functions are very common in many programming languages including Go.
Callbacks are essentially functions that are passed as arguments to other functions and are
intended to be called (or "executed") later in your program.
This first example shows a simple callback function. We're creating a function, `process`, that
takes a callback function as an argument. It then uses this callback to process an integer.
```go
package main
import "fmt"
func main() {
result := process(square, 5)
fmt.Println(result) // Output: 25
}
```
```go
package main
import "fmt"
func main() {
// Define an anonymous function that doubles the input.
doubler := func(n int) int {
return n * 2
}
result := process(doubler, 5)
fmt.Println(result) // Output: 10
}
```
3. **Callback in a Iteration**:
The third example iterates over a slice of integers, applying a callback function to each one.
```go
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
iterate(nums, tripler)
fmt.Println(nums) // Output: [3 6 9 12 15]
}
```
These examples demonstrate how to use callback functions in various contexts in Go, and
they should provide a good starting point for understanding this concept.
In Go, `type` is used to define new data types or to create an alias for an existing type.
When we write `type callbackFunc func(int) int`, we're defining a new type, `callbackFunc`,
which represents a function that takes an `int` as an argument and also returns an `int`.
This is why, in the `process` function, the `cb` parameter is of type `callbackFunc`. It's saying
that `cb` is a function that takes an `int` as an argument and returns an `int`.
So, when we pass the `square` function as the `cb` argument to the `process` function, Go
checks to see if `square` matches the `callbackFunc` type. Since `square` is defined as `func
square(n int) int` -- a function that takes an `int` as an argument and returns an `int` -- it
matches the `callbackFunc` type, and can be passed as the `cb` parameter to `process`.
This is essentially how types work in Go, and how we can define function types that can be
used as parameters for other functions. It provides a lot of flexibility and power when
structuring our programs, as it allows us to write functions that can take other functions as
arguments.
https://go.dev/play/p/o7klLEt0rEE
curriculum item # 164-hands-on-71-callback
package main
import (
"fmt"
"time"
)
// Function to be wrapped
func MyFunction() {
time.Sleep(2 * time.Second) // Simulate some work
fmt.Println("MyFunction completed")
}
func main() {
// Call the wrapped function
TimedFunction(MyFunction)
}
In the example above, the `TimedFunction` acts as a wrapper function that measures the
elapsed time taken by `MyFunction` to execute. It captures the start time, calls `MyFunction`,
calculates the elapsed time, and then prints it. By using the wrapper, you can easily add
timing functionality to multiple functions without modifying their implementation.
```go
func Logger(f func()) {
fmt.Println("Starting execution...")
f()
fmt.Println("Execution completed.")
}
func main() {
wrappedFunc := func() {
fmt.Println("Hello, World!")
}
Logger(wrappedFunc)
}
```
In the above example, the `Logger` function is a wrapper that adds logging statements before
and after the execution of the wrapped function `wrappedFunc`.
On the other hand, a callback function is a function that is passed as an argument to another
function and is invoked by that function at a specific point or event. The purpose of a callback
function is to allow the receiving function to execute the callback code when appropriate or
necessary. Callback functions are commonly used in event-driven programming or
asynchronous operations.
func main() {
data := []int{1, 2, 3, 4, 5}
ProcessData(data, callbackFunc)
}
```
In the above example, the `ProcessData` function takes a slice of integers and a callback
function. It iterates over the data and invokes the callback function for each item. In the `main`
function, we define the callback function as an anonymous function that simply prints the
number being processed.
https://go.dev/play/p/55s4mfHLMdz
curriculum item # 166-hands-on-73-wrapper
Pointers
In the Go programming language, a pointer refers to a variable that holds the memory
address. Pointers are a powerful feature that allows you to directly manipulate memory and
build complex data structures like linked lists, trees, and more. However, they also require
careful use, as improper pointer usage can lead to bugs and errors.
&
1. Address operator ( ): The & operator is used to get the memory address of a variable. If
`x` is an integer variable then `&x` will give you a pointer to `x` — that is, a memory address
where the integer `x` is stored.
*
2. Dereferencing operator ( ): The * operator is used to get the value stored at a memory
address. If `p` is a pointer to an integer then `*p` gives you the integer that `p` points to.
In this example, `p` is a pointer to `i`. You can get the value of `i` by dereferencing `p` with
`*p`, and you can change the value of `i` by assigning to `*p`.
Pointers also come into play when you're dealing with functions. If you pass a variable to a
function in Go, the function gets a copy of the variable — everything in go is pass by value —
if the function changes the variable, it won't affect the original. But if you pass a pointer to a
variable, the function can dereference the pointer to change the original variable.
Lastly, pointers are crucial when dealing with structs in Go. Since Go doesn’t have classes
and objects, you can use structs to create complex types, and you can use pointers to structs
to modify these structs or to avoid copying the structs around.
https://go.dev/play/p/rvcrIhHRntg
curriculum item # 167-what-are-pointers
For example, if you have a pointer to an integer, you can print its type and value like this:
```go
package main
import "fmt"
func main() {
num := 42
numPtr := &num
fmt.Printf("Type of numPtr: %T\n", numPtr)
fmt.Printf("Value of numPtr: %v\n", numPtr)
}
In this code, `numPtr` is a pointer to an integer. The `%T` verb will print the type of `numPtr`,
which is `*int`, and the `%v` verb will print the value of `numPtr`, which is the memory address of
`num`.
Note: `%v` will print the memory address where the pointer is pointing to. curriculum item #
https://go.dev/play/p/5MqydXuf3pB
curriculum item # 168-printf-pointer-value-type
Dereferencing pointers
Continuing the description from above, if you want to print the value that the pointer is pointing
to, you would need to dereference the pointer using `*` like this:
```go
fmt.Printf("Value pointed to by numPtr: %v\n", *numPtr)
```
This will print the value of `num`, which is `42`.
https://go.dev/play/p/RAT8EbhVg4J
curriculum item # 169-dereferencing
1. Pointers: A pointer holds the memory address of a value. It allows you to directly access and
modify the memory location of a value.
2. Slices: A slice is a descriptor of an array segment. It includes a pointer to the array, the
length of the segment, and its capacity (the maximum length of the segment).
3. Maps: A map is a powerful data structure that associates values of one type (the key) with
values of another type (the value). It's an unordered collection of key-value pairs.
4. Channels: Channels are used for communication between goroutines (Go's term for
threads). They allow you to pass data from one goroutine to another.
Remember that when you assign a reference type to another variable, the new variable
references the same memory location.
In Go, all data is passed by value, which means that whenever you pass data to a function, Go
creates a copy of that data and assigns the copy to a parameter variable. The function can do
whatever it wants to the copy without affecting the original data.
Mutability = changeable
However, mutability plays a critical role when you consider how "pass by value" works in
combination with more composite / aggregate / complex data types like slices and maps, and
pointers.
A mutable value is a value that can be changed. In Go, slices, maps, and pointers are
mutable data types. Even though they are passed by value, they still behave as if they were
passed by reference because the "value" that is copied and passed is the reference to the
underlying data, not the actual data.
```go
nums := []int{1, 2, 3}
```
```go
func modify(s []int) {
s[0] = 100
}
```
```go
modify(nums)
fmt.Println(nums)
You'll see `[100, 2, 3]`, not `[1, 2, 3]`. This is because the slice header, which includes the
pointer to the underlying array, is passed by value. But this copied value (the pointer) still
points to the same underlying array. So changes made inside the function are visible outside of
it, even though the data was passed by value.
In the case of pointers, the pointer itself is passed by value (meaning the function gets a copy
of the address), but the data it points to is the same. Therefore, dereferencing the pointer and
modifying the value it points to inside the function will modify the original value.
Remember, immutable data types like int, float, string, etc. are safe from this. When passed by
value, any changes made in the function won't affect the original value.
In summary, in Go, everything is passed by value. However, for mutable data types, the
"value" that's passed includes a reference to the underlying data, which is why changes
made inside the function are visible outside of it.
https://go.dev/play/p/pOn2VmMQ2Fy
curriculum item # 170-pass-by-value-mutability
Value semantics in Go refers to when the actual data of a variable is passed to a function or
assigned to another variable. This means that the new variable or function parameter gets a
completely independent copy of the data - EVERYTHING IN GO IS 'PASS BY VALUE'.
```go
package main
import "fmt"
func main() {
i := 1
fmt.Println(addOne(i)) // Prints: 2
fmt.Println(i) // Prints: 1
}
In this example, the function `addOne` doesn't modify the original `i` variable because it's
operating on a copy of `i`.
Pointer Semantics
Pointer semantics in Go, on the other hand, involve passing the memory address (a "pointer")
rather than the data itself. This means that you can modify the original data, not just a copy of
it. EVERYTHING IN GO IS 'PASS BY VALUE'.
```go
package main
import "fmt"
func main() {
i := 1
addOne(&i)
fmt.Println(i) // Prints: 2
}
```
In this example, the `addOne` function modifies the original `i` variable, because it's operating
on a pointer to `i`, not a copy
The choice between pointer and value semantics in Go depends on the specific needs of
your program.
● Value semantics are simpler and often safer because they avoid side effects, but
they can be inefficient for large data structures because they involve copying data.
● Pointer semantics can be more efficient and flexible, but they also require careful
management to avoid bugs related to shared, mutable state.
There are some general guidelines you can follow when deciding whether to use pointer or
value semantics in Go:
4. Consistency:
● It's important to be consistent. If some functions on a type use pointer semantics and
others use value semantics, it can lead to confusion. Typically, once a type has a
method with pointer semantics, all methods on that type should have pointer
semantics.
Remember, these are just guidelines. The specifics can depend on the situation, and
sometimes you may have good reasons to make different choices. But these guidelines
provide a good starting point.
● VALUE semantics
○ value semantics facilitate higher levels of integrity
■ "the majority of bugs that we get in software have to do with the
mutation of memory" ~ Bill Kennedy
Everything in Go is pass by value. Drop any phrases and concepts you may have from
other languages. Pass by reference, pass by copy - forget those phrases. “Pass by value.”
That is the only phrase you need to know and remember. That is the only phrase you should
use. Pass by value. Everything in Go is pass by value. In Go, what you see is what you get
(wysiwyg). Look at what is happening. That is what you get.
curriculum item # 172-pointer-value-semantics-heuristics
When you use pointer semantics, it's more likely for values to escape to the heap.
Escape analysis
It's important to note that the Go's escape analysis decides whether a variable should be
allocated on the stack or the heap. Escape analysis examines the function to see if the pointer
to a variable is being returned or if the variable is captured within a function literal (closure). If
so, the variable escapes to the heap. In other cases, even with pointer semantics, if the
variable does not escape, it may be kept on the stack.
To see escape analysis in Go, you use the '-gcflags' flag followed by '-m' when running the `go
Here's an example:
```bash
go run -gcflags '-m' main.go
```
```bash
go run -gcflags '-m=2' main.go
```
Inlining Decisions
Inlining in programming is an optimization that involves replacing a function call site with the
body of the called function. In the Go compiler, inlining decisions refer to the choices made
by the compiler about when and where to apply this optimization.
Inlining can often make a program run faster due to several reasons:
1. It can save the overhead of a function call (i.e., the time it takes to jump to the function
code, set up the function's local variables, and then jump back when the function is complete).
2. It can make further optimizations possible. Once the code from the function is inserted into
the calling function, the compiler may be able to see opportunities to optimize this larger block
of code that weren't visible when the code was split across multiple functions.
On the other hand, inlining too much code can lead to a larger binary, and possibly, due to
cache effects, slower code. Hence, the Go compiler makes inlining decisions to strike a
balance, inlining some small and frequently called functions while leaving larger and less
frequently called ones alone.
When you run the Go compiler with the '-m' flag, as in `-gcflags='-m'`, it will print information
about its inlining decisions along with escape analysis information. You'll see lines like
"inlining call to X" where X is a function that the compiler decided to inline.
Remember, the decision of using pointer or value semantics should be more about
sharing and mutating state rather than memory allocation and performance, except for
performance-critical parts of the code or handling very large data structures.
In Go, a 'method set' is the set of methods attached to a type. This concept is key to the
Go's interface mechanism, and it is associated with both the value types and pointer types.
● The method set of a type T consists of all methods with receiver type T.
○ These methods can be called using variables of type T.
The idea of the method set is integral to how interfaces are implemented and used in Go.
An interface in Go defines a method set, and any type whose method set is a superset of the
interface's method set is considered to implement that interface.
A crucial thing to remember is that in Go, if you define a method with a pointer receiver, the
method is only in the method set of the pointer type. This is important in the context of
interfaces because if an interface requires a method that's defined on the pointer (not the
value), then you can only use a pointer to that type to satisfy the interface, not a value of the
type.
```go
type T struct {
}
func (t T) M1() {
// ...
}
type X interface {
func main() {
var n X
t := T{}
n = t // This will be a compile error, because T does not implement M2()
tPtr := &T{}
n = tPtr // This is fine, *T implements M1() and M2()
}
```
In this example, `T` implements `M1()` and `*T` implements both `M1()` and `M2()`. Therefore,
`*T` satisfies interface `X`, but `T` does not. So we can assign `&T{}` to `n`, but not `T{}`.
https://go.dev/play/p/ixftmbd00IV
curriculum item # 174-method-sets-part-1
● The method set of a type T consists of all methods with receiver type T.
○ These methods can be called using variables of type T.
Hands-on exercises
HINT:
In Go, a value of type `T` can use a method with a receiver of type `*T` but with some
limitations.
If the method has a pointer receiver, `*T`, it means it needs to modify the receiver or the
receiver is a large struct that would be expensive to copy.
In this case, if you try to call this method using a value of type `T` (not `*T`), Go will
automatically take the address of that value and use that pointer as the receiver. However,
it is important to note that this is possible only if the value of type `T` is addressable, i.e., it is
```go
type T struct {
name string
}
func main() {
var myT T
myT.changeName("new name") // Works fine, Go automatically takes the address of myT
fmt.Println(myT.name) // Prints "new name"
So while a value of type `T` can use a method with a receiver of type `*T`, this only applies to
addressable values of type `T`. If the value isn't addressable, you'll get a compile-time error.
FYI, certain things are addressable and certain things are not. Generally, you can take the
address of variables, elements of an array or slice, fields of an addressable struct, and you
can't take the address of constants, literals (like `T{"another name"}`), or the return values of
functions or expressions (unless they return a pointer type or an interface type).
The reason that the struct literal `T{"another name"}` is not addressable is because it's a
temporary value in memory. It's not stored in a variable or a field, it's just a transient piece of
data that's created on the fly and discarded immediately after. It's not allocated a stable
location in memory that you can reference, hence it doesn't have an address you can take.
If you want to call a method that has a pointer receiver on a struct, you can create a pointer to
the literal directly:
```go
(&T{"another name"}).changeName("new name")
```
But remember that any changes made inside `changeName` method will not persist outside
the scope of the method call because the original `T` value is a temporary value. This is
different from the case when you have an addressable variable where the changes made by
the method will persist as they're modifying the actual memory location where the variable is
stored.
There are three types in go: builtin, reference, and structs (user defined type). The zero value
of a reference type is nil
https://go.dev/play/p/TDnuZTYbHdp
curriculum item # 179-hands-on-77
Generics
Generics tutorial
tutorial
● https://go.dev/doc/tutorial/generics
curriculum item # 184-generics-tutorial
Examples of concrete types in Go include `int`, `bool`, `string`, `float64`, arrays, slices, maps,
and structs, among others. These types have specific, predetermined storage layouts and
characteristics.
This is in contrast to an "interface type" in Go, which defines a contract (set of methods or
types) but does not represent a specific data layout or instance. Interface types are
abstract—they represent behavior or type but not a specific set of values.
Concrete type
For example, you can declare a struct with a specific set of fields and then create an instance of
this struct. This is a concrete type.
```go
type Employee struct {
Name string
Age int
}
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
```
In the latter case, `io.Reader` is an interface type, and any type that implements the `Read`
method with the correct signature is said to "satisfy" the `io.Reader` interface.
curriculum item # 185-concrete-types-interface-types
Application
JSON documentation
We understand pointers; we understand methods. We now have enough knowledge to begin
using the standard library. This video will give you an orientation to how I approach, read,
and work with the standard library. I made a big assumption here that JSON and
marshalling would be understood. If this is new to you, read these notes from a fellow
student (
https://docs.google.com/document/d/1gqTe1ouyvGXKaD1kBBN9hNbTaDBSGZ6Ynt3GBF
UbtM0/edit?usp=sharing) .
video: 118
JSON marshal
Here is an example of how you marshal data in Go to JSON. Also important, this video shows
how the case of an identifier - lowercase or uppercase, determines whether or not the data can
be exported.
code:
● https://play.golang.org/p/jtE_n16cQO
video: 119
JSON unmarshal
We can take JSON and bring it back into our Go program by unmarshalling that JSON.
Great resources for understanding and working with JSON are shared.
cool websites:
Todd McLeod - Learn To Code Go - Page 127
● http://rawgit.com/
● https://mholt.github.io/json-to-go/
● https://github.com/goestoeleven
code:
● understanding JSON rawgit HTML page
● https://play.golang.org/p/O9q0DmpzsZ
video: 120
Writer interface
Understanding the writer interface from package io. Also, one last note about working with
JSON: encode & decode.
code: https://play.golang.org/p/3Txh-dKQBf
video: 121
Sort
The sort package allows us to sort slices.
code:
● starting code:
○ https://play.golang.org/p/igIGnMv6AN
● sorted
○ https://play.golang.org/p/8UkvEdzQOk
video: 122
Sort custom
Here is how we sort on fields in a struct.
code:
● starting code:
○ https://play.golang.org/p/UhXN-G2FwY
● sorted
○ by age: https://play.golang.org/p/kqmJovOU5V
○ by name: https://play.golang.org/p/he70VcFmdM
video: 123
bcrypt
All too often today you still hear about websites and databases being hacked and user’s
information being compromised. There is no excuse for this. As programmers, we have the tools
to protect user data. Bcrypt is one of the tools you can use to protect user data. Using bcrypt,
we will gain further understanding as to how to read and implement code from the
standard library.
code:
Hands-on exercises
Hands-on exercise #1
Starting with this code, marshal the []user to JSON. There is a little bit of a curve ball in this
hands-on exercise - remember to ask yourself what you need to do to EXPORT a value from a
package.
solution: https://play.golang.org/p/8BK6PXj3aG
video: 125
Hands-on exercise #2
Starting with this code, unmarshal the JSON into a Go data structure. Hint:
https://mholt.github.io/json-to-go/
code:
● code setup (just fyi, not needed for exercise):
○ https://play.golang.org/p/nWPP5Z2Q4e
● solution:
○ https://play.golang.org/p/r8oSG8DIPR
video: 126
Hands-on exercise #3
Starting with this code, encode to JSON the []user sending the results to Stdout. Hint: you will
need to use json.NewEncoder(os.Stdout).encode(v interface{})
solution: https://play.golang.org/p/ql90D1OQqd
video: 127
Hands-on exercise #4
Starting with this code, sort the []int and []string for each person.
solution: https://play.golang.org/p/jz_llY1aPp
video: 128
Hands-on exercise #5
Starting with this code, sort the []user by
● age
● last
Concurrency
Concurrency vs parallelism
But when people hear the word concurrency they often think of parallelism, a related but
quite distinct concept. In programming, concurrency is the composition of independently
executing processes, while parallelism is the simultaneous execution of (possibly
related) computations. Concurrency is about dealing with lots of things at once.
Parallelism is about doing lots of things at once.
● First Intel dual-core processor: 2006. Google begins developing a language to
take advantage of multiple cores: 2007
video: 130
WaitGroup
A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to
set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when
finished. At the same time, Wait can be used to block until all goroutines have finished. Writing
concurrent code is super easy: all we do is put “go” in front of a function or method call.
● runtime.NumCPU()
● runtime.NumGoroutine()
● sync.WaitGroup
○ func (wg *WaitGroup) Add(delta int)
○ func (wg *WaitGroup) Done()
○ func (wg *WaitGroup) Wait()
race conditions picture:
Receivers Values
-----------------------------------------------
(t T) T and *T
(t *T) *T
code: https://play.golang.org/p/KK8gjsAWBZ
video: 132
Documentation
They're called goroutines because the existing terms—threads, coroutines, processes, and so
on—convey inaccurate connotations. A goroutine has a simple model: it is a function executing
concurrently with other goroutines in the same address space.
code:
● https://play.golang.org/p/lBKFKCwrue
video: 133
Race condition
Here is a picture of the race condition we are going to create:
Mutex
A “mutex” is a mutual exclusion lock. Mutexes allow us to lock our code so that only one
goroutine can access that locked chunk of code at a time.
code: https://github.com/GoesToEleven/golang-web-dev/tree/master/000_temp/52-race-condition
video: 135
Atomic
We can use package atomic to also prevent a race condition in our incrementer code.
code: https://github.com/GoesToEleven/golang-web-dev/tree/master/000_temp/52-race-condition
Hands-on exercises
Hands-on exercise #1
● in addition to the main goroutine, launch two additional goroutines
○ each additional goroutine should print something out
● use waitgroups to make sure each goroutine finishes before your program exists
code: https://github.com/GoesToEleven/go-programming
video: 148
Hands-on exercise #2
This exercise will reinforce our understanding of method sets:
● create a type person struct
● attach a method speak to type person using a pointer receiver
○ *person
● create a type human interface
○ to implicitly implement the interface, a human must have the speak method
● create func “saySomething”
○ have it take in a human as a parameter
○ have it call the speak method
● show the following in your code
○ you CAN pass a value of type *person into saySomething
○ you CANNOT pass a value of type person into saySomething
● here is a hint if you need some help
○ https://play.golang.org/p/FAwcQbNtMG
Receivers Values
-----------------------------------------------
(t T) T and *T
(t *T) *T
code: https://github.com/GoesToEleven/go-programming
video: 149
Hands-on exercise #3
● Using goroutines, create an incrementer program
○ have a variable to hold the incrementer value
○ launch a bunch of goroutines
■ each goroutine should
Todd McLeod - Learn To Code Go - Page 134
● read the incrementer value
○ store it in a new variable
● yield the processor with runtime.Gosched()
● increment the new variable
● write the value in the new variable back to the incrementer
variable
● use waitgroups to wait for all of your goroutines to finish
● the above will create a race condition.
● Prove that it is a race condition by using the -race flag
● if you need help, here is a hint: https://play.golang.org/p/FYGoflKQej
code: https://github.com/GoesToEleven/go-programming
video: 150
Hands-on exercise #4
Fix the race condition you created in the previous exercise by using a mutex
● it makes sense to remove runtime.Gosched()
code: https://github.com/GoesToEleven/go-programming
video: 151
Hands-on exercise #5
Fix the race condition you created in exercise #4 by using package atomic
code: https://github.com/GoesToEleven/go-programming
video: 152
Hands-on exercise #6
Create a program that prints out your OS and ARCH. Use the following commands to run it
● go run
● go build
● go install
code: https://github.com/GoesToEleven/go-programming
video: 153
Channels
**Goroutines**
A Goroutine is a lightweight thread of execution managed by the Go runtime. It's like a thread in
other languages, but it's much cheaper in terms of memory and startup time. You can start a
Goroutine by simply using the `go` keyword before a function call:
```go
go funcName() // funcName runs concurrently with the rest of the program
```
Goroutines have the potential to run concurrently, and they're scheduled by the Go runtime, not
the operating system, which makes them very efficient. But with Goroutines alone, you can run
into problems such as race conditions, where two Goroutines access shared state concurrently.
Channels
This is where channels come in. Channels are a synchronization primitive that allow Goroutines
to communicate and synchronize execution. You can send values into a channel and receive
those values into another Goroutine. This allows you to coordinate tasks and send data safely
between Goroutines:
```go
ch := make(chan int) // Make a channel of type int
go func() {
ch <- 1 // Send the value 1 into the channel
}()
The concepts of Goroutines and channels are inspired by a formal language and model of
computation known as Communicating Sequential Processes (CSP), developed by Sir Tony
Hoare. CSP provides a way to describe patterns of interaction in concurrent systems. It's a
fundamental part of the design of the Go language.
In CSP and in Go, there's no need for locks or condition variables to manage shared state
among concurrent threads or processes. Instead, data is sent between Goroutines via channels,
and by default, these communications are synchronized: when a value is sent via a channel, the
sending Goroutine waits until another Goroutine receives the value.
This approach to concurrency is often summarized by the phrase "Do not communicate by
sharing memory; instead, share memory by communicating." This means it's preferred to avoid
having multiple Goroutines with access to shared memory and instead use channels to pass the
ownership of data between them, preventing race conditions.
By adhering to the principles of CSP, Go provides a rich and expressive framework for writing
concurrent and parallel programs that are also safe and easy to reason about.
Understanding channels
Channels Introduction
● making a channel
c := make(chan int)
● putting values on a channel
c <- 42
● taking values off of a channel
<-c
● buffered channels
c := make(chan int, 4)
● channels block
○ they are like runners in a relay race
■ they are synchronized
■ they have to pass/receive the value at the same time
● just like runners in a relay race have to pass / receive the
baton to each other at the same time
○ one runner can’t pass the baton at one moment
○ and then, later, have the other runner receive the
baton
○ the baton is passed/received by the runners at the
same time
Directional channels
Channel type. Read from left to right.
code:
● seeing the type
○ code from previous video
■ https://play.golang.org/p/a98otBr4eX
○ send & receive (bidirectional)
■ https://play.golang.org/p/di1mKkGF6Y
■ “send and receive” means “send and receive”
● https://play.golang.org/p/SHr3lpX4so
○ already saw the above code
■ send means send
● error: “invalid operation: <-cs (receive from send-only type chan<- int)”
○ https://play.golang.org/p/oB-p3KMiH6
■ receive means receive
● error: “invalid operation: cr <- 42 (send to receive-only type <-chan int)”
○ https://play.golang.org/p/_DBRueImEq
Using channels
● create general channels
● in funcs you can specify
○ receive channel
■ you can receive values from the channel
■ a receive channel parameter
■ in the func, you can only pull values from the channel
■ you can’t close a receive channel
○ send channel
■ you can push values to the channel
■ you can’t receive/pull/read from the channel
■ you can only push values to the channel
code:
● https://play.golang.org/p/t1rc8rSrMd
code: https://github.com/GoesToEleven/go-programming
video: 157
Range
Range stops reading from a channel when the channel is closed.
code:
● close a channel
○ https://play.golang.org/p/w4KMBpSEyj
● range over a channel
Select
Select statements pull the value from whatever channel has a value ready to be pulled.
code:
● building on previous code
○ https://play.golang.org/p/41m5Mt7B-5
● not closing even odd channels
○ https://play.golang.org/p/Emx6S4zn_y
code: https://github.com/GoesToEleven/go-programming
video: 159
Comma ok idiom
The comma ok idiom with select.
code:
● closing quit channel & comma ok idiom
○ https://play.golang.org/p/fomc4Sc-gz
○ with bool
■ https://play.golang.org/p/QAwBLDKZuq
○ with int
■ https://play.golang.org/p/6XaTWxKpU3
● just comma ok idiom
○ https://play.golang.org/p/6LPzCtZeT3
○ https://play.golang.org/p/dToDc0zJhZ
● clean up above code - comma ok idiom
○ step 1 - comma ok idiom code reduced
■ https://play.golang.org/p/60K5-7xat6
○ step 2 - remove underscore variable throwaways
■ https://play.golang.org/p/QWzGUISkJK
○ step 3 - change quit from bool to int
■ https://play.golang.org/p/tFlvGg9ENT
● select receive
○ https://play.golang.org/p/62vRH3jeQ6
● select send
○ https://play.golang.org/p/bv_vCcJ6zs
● interesting
○ doesn’t run
■ https://play.golang.org/p/o7Quy6wc6r
Fan in
Taking values from many channels, and putting those values onto one channel.
code:
● Todd’s code
○ https://play.golang.org/p/_CyyXQBCHe
● Rob Pike’s code
○ https://play.golang.org/p/buy30qw5MM
video: 161
Fan out
Taking some work and putting the chunks of work onto many goroutines.
code:
● fan out in
○ https://play.golang.org/p/iU7Oee2nm7
● throttle throughput
○ https://play.golang.org/p/RzR3Kjrx7q
video: 162
Context
In Go servers, each incoming request is handled in its own goroutine. Request handlers often
start additional goroutines to access backends such as databases and RPC services. The set of
goroutines working on a request typically needs access to request-specific values such as the
identity of the end user, authorization tokens, and the request's deadline. When a request is
canceled or times out, all the goroutines working on that request should exit quickly so the
system can reclaim any resources they are using. At Google, we developed a context package
that makes it easy to pass request-scoped values, cancellation signals, and deadlines across
API boundaries to all the goroutines involved in handling a request. The package is publicly
available as context. This article describes how to use the package and provides a complete
working example.
further reading:
● https://blog.golang.org/context
Hands-on exercises
Hands-on exercise #1
● get this code working:
○ using func literal, aka, anonymous self-executing func
■ solution: https://play.golang.org/p/SHr3lpX4so
○ a buffered channel
■ solution: https://play.golang.org/p/Y0Hx6IZc3U
video: 164
Hands-on exercise #2
● Get this code running:
Hands-on exercise #3
● Starting with this code, pull the values off the channel using a for range loop
solution: https://play.golang.org/p/D3N4Tq54SN
video: 166
Hands-on exercise #4
● Starting with this code, pull the values off the channel using a select statement
solution: https://play.golang.org/p/FulKBY5JNj
video: 167
Hands-on exercise #5
● Show the comma ok idiom starting with this code.
solution: https://play.golang.org/p/qh2ywLB5OG
video: 168
Hands-on exercise #6
● write a program that
○ puts 100 numbers to a channel
○ pull the numbers off the channel and print them
solution: https://play.golang.org/p/096Lk1BR7o
video: 169
Hands-on exercise #7
● write a program that
○ launches 10 goroutines
■ each goroutine adds 10 numbers to a channel
○ pull the numbers off the channel and print them
solutions:
● https://play.golang.org/p/R-zqsKS03P
● https://play.golang.org/p/quWnlwzs2z
● https://go.dev/play/p/WqYnBC_CiKn
video: 170
Understanding
● https://golang.org/doc/faq#exceptions
○ Go does not favor try / catch / finally
● https://en.wikipedia.org/wiki/Exception_handling#Criticism
○ Notice Hoare’s work also influenced goroutines and channels
● https://blog.golang.org/error-handling-and-go
video: 171
Checking errors
Write the code with errors before writing the code without errors. Always check for errors.
Always, always, always.*
(*almost always)
code:
● https://github.com/GoesToEleven/go-programming
video: 172
Recover
https://blog.golang.org/defer-panic-and-recover
code:
● https://play.golang.org/p/HI4uG55ait
● https://play.golang.org/p/ZocncqtwaK
Hands-on exercises
Hands-on exercise #1
Start with this code. Instead of using the blank identifier, make sure the code is checking and
handling the error.
solution:
● https://play.golang.org/p/tn8oiuL1Yn
video: 176
Hands-on exercise #2
Start with this code. Create a custom error message using “fmt.Errorf”.
solution:
● https://play.golang.org/p/HugU4HJEEO
● https://play.golang.org/p/NII-lmGasj
● https://play.golang.org/p/Vo5kIoR-sG
video: 177
Hands-on exercise #3
Create a struct “customErr” which implements the builtin.error interface. Create a func “foo” that
has a value of type error as a parameter. Create a value of type “customErr” and pass it into
“foo”. If you need a hint, here is one.
solution:
● https://play.golang.org/p/ixeowY2fd2
● assertion
○ https://play.golang.org/p/pbl2kCYsM0
● conversion
Hands-on exercise #4
Starting with this code, use the sqrt.Error struct as a value of type error. If you would like, use
these numbers for your
● lat "50.2289 N"
● long "99.4656 W"
solution:
● https://play.golang.org/p/nsRxbDfkCh
video: 179
Hands-on exercise #5
We are going to learn about testing next. For this exercise, take a moment and see how much
you can figure out about testing by reading the testing documentation & also Caleb Doxsey’s
article on testing. See if you can get a basic example of testing working.
video: 180
Writing documentation
Introduction
Before writing documentation, we are going to look at reading documentation. There are several
things to know about documentation:
● godoc.org
○ standard library and third party package documentation
● golang.org
○ standard library documentation
● go doc
○ command to read documentation at the command line
● godoc
○ command to read documentation at the command line
○ also can run a local server showing documentation
Personal update on my health, mortality, and resources that have helped me cultivate a more
skillful mindset in my life:
● Never Split The Difference by Chris Voss
● Grit: The Power of Passion and Perseverance by Angela Duckworth
● Smarter Faster Better: The Secrets of Being Productive in Life and Business
● https://www.entrepreneur.com/topic/masters-of-scale
● http://dharmaseed.org/teachers/
video: 180-a
Todd McLeod - Learn To Code Go - Page 146
go doc
go doc prints the documentation for a package, const, func, type, var, or method
● go doc accepts zero, one, or two arguments.
○ zero
■ prints package documentation for the package in the current directory
○ go doc
○ one
■ argument Go-syntax-like representation of item to be documented
● fyi: <sym> also known as “identifier”
○ go doc <pkg>
○ go doc <sym>[.<method>]
○ go doc [<pkg>.]<sym>[.<method>]
○ go doc [<pkg>.][<sym>.]<method>
● The first item in this list that succeeds is the one whose
documentation is printed. If there is a symbol but no package, the
package in the current directory is chosen. However, if the
argument begins with a capital letter it is always assumed to be a
symbol in the current directory.
○ two
■ first argument must be a full package path
○ go doc <pkg> <sym>[.<method>]
● examples
godoc
Godoc extracts and generates documentation for Go programs. It has two modes
● without -http flag
○ command-line mode; prints text documentation to standard out and exits
○ -src flag
■ godoc prints the exported interface of a package in Go source form, or the
implementation of a specific exported language
godoc.org
● put the url of your code into godoc
○ your documentation will appear on godoc
○ “refresh” at bottom of page if it is ever out of date
video: 180-d
Writing documentation
Documentation is a huge part of making software accessible and maintainable. Of course it
must be well-written and accurate, but it also must be easy to write and to maintain. Ideally, it
should be coupled to the code itself so the documentation evolves along with the code. The
easier it is for programmers to produce good documentation, the better for everyone.
● https://blog.golang.org/godoc-documenting-go-code
○ godoc parses Go source code - including comments - and produces
documentation as HTML or plain text. The end result is documentation tightly
coupled with the code it documents. For example, through godoc's web interface
you can navigate from a function's documentation to its implementation
with one click.
○ comments are just good comments, the sort you would want to read even
if godoc didn't exist.
○ to document
■ a type, variable, constant, function, or package,
■ write a comment directly preceding its declaration, with no
intervening blank line.
● begin with the name of the element
● for packages
○ first sentence appears in package list
○ if a large amount of documentation, place in its own
file doc.go
Todd McLeod - Learn To Code Go - Page 149
■ example: package fmt
○ the best thing about godoc's minimal approach is how easy it is to use. As
a result, a lot of Go code, including all of the standard library, already
follows the conventions.
● example
○ errors package
code: https://github.com/GoesToEleven/go-programming
video: 180-e
Hands-on exercises
Hands-on exercise #1
Create a dog package. The dog package should have an exported func “Years” which takes
human years and turns them into dog years (1 human year = 7 dog years). Document your
code with comments. Use this code in func main.
● run your program and make sure it works
● run a local server with godoc and look at your documentation.
solution: https://github.com/GoesToEleven/go-programming
video: 180-f
Hands-on exercise #2
Push the code to github. Get your documentation on godoc.org and take a screenshot. Delete
your code from github. Refresh godoc.org so that it no longer has your code.
solution: https://github.com/GoesToEleven/go-programming
video: no video
Hands-on exercise #3
Use godoc at the command line to look at the documentation for:
● fmt
● fmt Print
● strings
● strconv
solution: https://github.com/GoesToEleven/go-programming
video: no video
Introduction
Tests must
● be in a file that ends with “_test.go”
● put the file in the same package as the one being tested
● be in a func with a signature “func TestXxx(*testing.T)”
Run a test
● go test
Deal with test failure
● use t.Error to signal failure
Nice idiom
● expected
● got
code: https://github.com/GoesToEleven/go-programming
video: 181
Table tests
We can write a series of tests to run. This allows us to test a variety of situations.
code: https://github.com/GoesToEleven/go-programming
video: 182
Example tests
Examples show up in documentation.
● godoc -http :8080
● https://blog.golang.org/examples
● go test ./…
code: https://github.com/GoesToEleven/go-programming
video: 183
Golint
● gofmt
○ formats go code
● go vet
○ reports suspicious constructs
● golint
○ reports poor coding style
Benchmark
Part of the testing package allows us to measure the speed of our code. This could also be
called “measuring the performance” of your code, or “benchmarking” your code - finding out how
fast the code runs.
code: https://github.com/GoesToEleven/go-programming
video: 185
Coverage
Coverage in programming is how much of our code is covered by tests. We can use the “-cover”
flag to run coverage analysis on our code. We can use the flag and required file name
“-coverprofile <some file name>” to write our coverage analysis to a file.
code:
● go test -cover
○ go test -coverprofile c.out
■ show in browser:
● go tool cover -html=c.out
■ learn more
● go tool cover -h
● https://github.com/GoesToEleven/go-programming
video: 186
Benchmark examples
Here are a few examples showing benchmarking in action. This includes comparing manual
concatenation with strings.Join
code:
● https://github.com/GoesToEleven/go-programming
video: 187
Review
Here is a review of the different commands useful with benchmarks, examples, and tests.
● godoc -http=:8080
● go test
● go test -bench .
○ don’t forget the “.” in the line above
● go test -cover
● go test -coverprofile c.out
● go tool cover -html=c.out
code:
Hands-on exercises
Hands-on exercise #1
Start with this code. Get the code ready to BET on the code (add benchmarks, examples, tests).
Run the following in this order:
● tests
● benchmarks
● coverage
● coverage shown in web browser
● examples shown in documentation in a web browser
solution:
● https://github.com/GoesToEleven/go-programming
video: 189
Hands-on exercise #2
Start with this code. Get the code ready to BET on (add benchmarks, examples, tests) however
do not write an example for the func that returns a map; and only write a test for it as an extra
challenge. Add documentation to the code. Run the following in this order:
● tests
● benchmarks
● coverage
● coverage shown in web browser
● examples shown in documentation in a web browser
solution:
● https://github.com/GoesToEleven/go-programming
video: 190
Hands-on exercise #3
Start with this code. Get the code ready to BET on (add benchmarks, examples, tests). Write a
table test. Add documentation to the code. Run the following in this order:
● tests
● benchmarks
● coverage
● coverage shown in web browser
● examples shown in documentation in a web browser
helpful to know:
● https://play.golang.org/p/4GUqs1HMpp
Todd McLeod - Learn To Code Go - Page 153
● https://play.golang.org/p/P9unTIFeOq
solution:
● https://github.com/GoesToEleven/go-programming
video: 191
FAREWELL
You have done great work - the greatest work. You have taken steps to create a better life for
yourself, and for others. As an individual improves their own life, they improve the world. The
skills you are acquiring are some of the most valuable skills demanded today: knowing how to
code and knowing how to use the Go programming language.
Next Steps
video: 192
Cross compile
● GOOS & GOARCH
○ http://godoc.org/runtime#pkg-constants
● GOOS=darwin GOARCH=386 go build test.go
video: 146
Packages
● one folder, many files
○ package declaration in every file
○ package scope
■ something in one file is accessible to another file
○ imports have file scope
● exported / unexported
○ aka, visible / not visible
○ we don’t say (generally speaking): public / private
○ capitalization
■ capitalize: exported, visible outside the package
■ lowercase: unexported, not visible outside the package
● How To Write Packages in Go
○ https://www.digitalocean.com/community/tutorials/how-to-write-packages-in-go
○ "A package is made up of Go files that live in the same directory and have the
same package statement at the beginning. You can include additional
functionality from packages to make your programs more sophisticated. Some
packages are available through the Go Standard Library and are therefore
installed with your Go installation. Others can be installed with Go’s go get
● Idiomatic Go code
○ idioms
■ every language has its own language
● idioms are patterns of speech
■ “idiomatic go”
● when someone writes “idiomatic Go” they are writing Go code in
the way Go code community writes code
■ for example
● mechanical sympathy
● pass by value
Idiomatic Go code is code that follows the established conventions and best practices of the
Go programming language. These conventions and best practices are designed to make the
code more readable, maintainable, and efficient.
Notes - web:
● curl -i https://api.github.com/users/GoesToEleven
● https://http.cat/
Notes - dependency management:
● our software dependency problem russ cox
○ https://research.swtch.com/deps
Notes - go commands:
● go build -gcflags=-m
○ escape analysis
In the Go programming language, when you use the `go build -gcflags=-m` command with the
`-m` flag, it enables escape analysis and prints optimization decisions made by the compiler.
The output provides insights into memory allocation and the movement of objects between
the stack and the heap.
When you see the message "escapes to heap," it means that a variable has been allocated
on the heap instead of the stack. The heap is a region of memory used for dynamic memory
allocation, while the stack is a region of memory used for function calls and local variables.
Variables allocated on the stack have a shorter lifetime and are automatically deallocated
when they go out of scope, whereas heap-allocated variables persist until they are explicitly
deallocated.
"Moved to heap" indicates that an object, such as a value or a data structure, has been
dynamically allocated on the heap because it needs to outlive the current scope. This often
occurs when you assign a reference to a variable declared within a function to a variable
declared in an outer scope or when you return a pointer from a function. In such cases, the
object is allocated on the heap and the reference to it is moved from the stack to the heap.
The compiler's decision to allocate variables on the stack or the heap is influenced by escape
analysis, which determines whether a variable's reference "escapes" from the current scope.
If a variable's reference escapes, it means that it is accessed from outside the current scope,
By examining the output of `go build -gcflags=-m`, you can gain insights into how the Go
compiler optimizes memory usage and understand whether variables are allocated on the
stack or the heap based on their escape behavior.