This commit is contained in:
wagslane
2023-05-01 15:25:27 -06:00
parent f8912668b8
commit 9be3074de6
868 changed files with 58698 additions and 2 deletions

View File

@@ -1,2 +1,10 @@
# fcc-learn-golang-assets
A snapshot of the assets for the Learn Go course on FreeCodeCamp's youtube
# Assets for "Learn Go" on FreeCodeCamp
This is a snapshot of the code samples for the ["Learn Go" course](https://boot.dev/courses/learn-golang) on [Boot.dev](https://boot.dev) at the time the video for FreeCodeCamp was released on YouTube. If you want the most up-to-date version of the code, please visit the official [Boot.dev course](https://boot.dev/courses/learn-http). Otherwise, if you're looking for the files used in the video, you're in the right place!
* [Course code samples](/course)
* [Project steps](/project)
## License
You are free to use this content and code for personal education purposes. However, you are *not* authorized to publish this content or code elsewhere, whether for commercial purposes or not.

View File

@@ -0,0 +1,9 @@
package main
import "fmt"
func main() {
// single-line comments start with "//"
// comments are just for documentation - they don't execute
fmt.Println("hello world")
}

View File

@@ -0,0 +1,9 @@
package main
import "fmt"
func main() {
// single-line comments start with "//"
// comments are just for documentation - they don't execute
fmt.Println("starting Textio server")
}

View File

@@ -0,0 +1 @@
starting Textio server

View File

@@ -0,0 +1,11 @@
# Welcome to "Learn Go"
*This course assumes you're familiar with programming basics. If you're new to coding check out our [Learn Python course](https://boot.dev/learn/learn-python) first.*
![golang gopher](https://go.dev/blog/gopher/header.jpg)
## Booting up the "Textio" server
All the code you write in this course is part of a larger product: an SMS API called "Textio". Textio sends text messages over the internet, it's basically a cute Twilio clone.
Assignment: log `starting Textio server` to the console instead of `hello world`.

View File

@@ -0,0 +1,22 @@
package main
import "fmt"
func main() {
messagesFromDoris := []string{
"You doing anything later??",
"Did you get my last message?",
"Don't leave me hanging...",
"Please respond I'm lonely!",
}
numMessages := float64(len(messagesFromDoris))
costPerMessage := .02
// don't touch above this line
totalCost := costPerMessage + numMessages
// don't touch below this line
fmt.Printf("Doris spent %.2f on text messages today\n", totalCost)
}

View File

@@ -0,0 +1,22 @@
package main
import "fmt"
func main() {
messagesFromDoris := []string{
"You doing anything later??",
"Did you get my last message?",
"Don't leave me hanging...",
"Please respond I'm lonely!",
}
numMessages := float64(len(messagesFromDoris))
costPerMessage := .02
// don't touch above this line
totalCost := costPerMessage * numMessages
// don't touch below this line
fmt.Printf("Doris spent %.2f on text messages today\n", totalCost)
}

View File

@@ -0,0 +1 @@
Doris spent 0.08 on text messages today

View File

@@ -0,0 +1,5 @@
# Fix a Bug!
Textio users are reporting that we're billing them for wildly inaccurate amounts. They're *supposed* to be billed `.02` dollars for each text message sent.
**Fix the math bug on line 17.**

View File

@@ -0,0 +1,9 @@
{
"question": "Go code generally runs ____ than interpreted languages and compiles ____ than other compiled languages like C and Rust",
"answers": [
"faster, faster",
"faster, slower",
"slower, faster",
"slower, slower"
]
}

View File

@@ -0,0 +1,11 @@
# Go is fast, simple and productive
Generally speaking, compiled languages run much faster than interpreted languages, and Go is no exception.
Go is one of the fastest programming languages, beating JavaScript, Python, and Ruby handily in most benchmarks.
However, Go code doesn't *run* quite as fast as its compiled Rust and C counterparts. That said, it *compiles* much faster than they do, which makes the developer experience super productive. Unfortunately, there are no swordfights on Go teams...
![xkcd compiling](https://imgs.xkcd.com/comics/compiling.png)
*- comic by [xkcd](https://xkcd.com/303/)*

View File

@@ -0,0 +1,7 @@
{
"question": "Does Go generally execute faster than Rust?",
"answers": [
"No",
"Yes"
]
}

View File

@@ -0,0 +1,21 @@
# Comparing Go's Speed
Go is *generally* faster and more lightweight than interpreted or VM-powered languages like:
* Python
* JavaScript
* PHP
* Ruby
* Java
However, in terms of execution speed, Go does lag behind some other compiled languages like:
* C
* C++
* Rust
Go is a bit slower mostly due to its automated memory management, also known as the "Go runtime". Slightly slower speed is the price we pay for memory safety and simple syntax!
![speed comparison](https://miro.medium.com/max/2020/1*nlpYI256BR71xMBWd1nlfg.png)
Textio is an amazing candidate for a Go project. We'll be able to quickly process large amounts of text all while using a language that is safe and simple to write.

View File

@@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("the-compiled Textio server is starting"
}

View File

@@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("the-compiled Textio server is starting")
}

View File

@@ -0,0 +1 @@
the-compiled Textio server is starting

View File

@@ -0,0 +1,17 @@
# The Compilation Process
Computers need machine code, they don't understand English or even Go. We need to convert our high-level (Go) code into machine language, which is really just a set of instructions that some specific hardware can understand. In your case, your CPU.
The Go compiler's job is to take Go code and produce machine code. On Windows, that would be a `.exe` file. On Mac or Linux, it would be any executable file. The code you write in your browser here on Boot.dev is being compiled for you on Boot.dev's servers, then the machine code is executed in your browser as [Web Assembly](https://blog.boot.dev/golang/running-go-in-the-browser-with-web-assembly-wasm).
## A note on the structure of a Go program
We'll go over this all later in more detail, but to sate your curiosity for now, here are a few tidbits about the code.
1. `package main` lets the Go compiler know that we want this code to compile and run as a standalone program, as opposed to being a library that's imported by other programs.
2. `import fmt` imports the `fmt` (formatting) package. The formatting package exists in Go's standard library and let's us do things like print text to the console.
3. `func main()` defines the `main` function. `main` is the name of the function that acts as the entry point for a Go program.
## Assignment
To pass this exercise, fix the compiler error in the code.

View File

@@ -0,0 +1,7 @@
{
"question": "Do computer processors understand English instructions like 'open the browser'?",
"answers": [
"No",
"Yes"
]
}

View File

@@ -0,0 +1,33 @@
# Compiling explained
Computers don't know how to do anything unless we as programmers tell them what to do.
Unfortunately computers don't understand human language. In fact, they don't even understand uncompiled computer programs.
For example, the code:
```go
package main
import "fmt"
func main(){
fmt.Println("hello world")
}
```
means *nothing* to a computer.
## Computers need machine code
A computer's [CPU](https://en.wikipedia.org/wiki/Central_processing_unit) only understands its own *instruction set*, which we call "machine code".
Instructions are basic math operations like addition, subtraction, multiplication, and the ability to save data temporarily.
For example, an [ARM processor](https://en.wikipedia.org/wiki/ARM_architecture) uses the *ADD* instruction when supplied with the number `0100` in binary.
## Go, C, Rust, etc.
Go, C, and Rust are all languages where the code is first converted to machine code by the compiler before it's executed.
![compiler](https://www.astateofdata.com/wp-content/uploads/2019/09/code-compiler-machine-code.png)

View File

@@ -0,0 +1,7 @@
{
"question": "Do users of compiled programs need access to source code?",
"answers": [
"No",
"Yes"
]
}

View File

@@ -0,0 +1,24 @@
# Compiled vs Interpreted
Compiled programs can be run without access to the original source code, and without access to a compiler. For example, when your browser executes the code you write in this course, it doesn't use the original code, just the compiled result.
Note how this is different than interpreted languages like Python and JavaScript. With Python and JavaScript the code is interpreted at [runtime](https://en.wikipedia.org/wiki/Runtime_(program_lifecycle_phase)) by a separate program known as the "interpreter". Distributing code for users to run can be a pain because they need to have an interpreter installed, and they need access to the original source code.
## Examples of compiled languages
* Go
* C
* C++
* Rust
## Examples of interpreted languages
* JavaScript
* Python
* Ruby
![compiled vs interpreted](https://i.imgur.com/ovHaWmS.jpg)
## Why build Textio in a compiled language?
One of the most convenient things about using a compiled language like Go for Textio is that when we deploy our server we don't need to include any runtime language dependencies like Node or a Python interpreter. We just add the pre-compiled binary to the server and start it up!

View File

@@ -0,0 +1,9 @@
{
"question": "Which language is interpreted?",
"answers": [
"Python",
"Rust",
"Go",
"C++"
]
}

View File

@@ -0,0 +1,24 @@
# Compiled vs Interpreted
Compiled programs can be run without access to the original source code, and without access to a compiler. For example, when your browser executes the code you write in this course, it doesn't use the original code, just the compiled result.
Note how this is different than interpreted languages like Python and JavaScript. With Python and JavaScript the code is interpreted at [runtime](https://en.wikipedia.org/wiki/Runtime_(program_lifecycle_phase)) by a separate program known as the "interpreter". Distributing code for users to run can be a pain because they need to have an interpreter installed, and they need access to the original source code.
## Examples of compiled languages
* Go
* C
* C++
* Rust
## Examples of interpreted languages
* JavaScript
* Python
* Ruby
![compiled vs interpreted](https://i.imgur.com/ovHaWmS.jpg)
## Deploying compiled programs is generally more simple
One of the most convenient things about using a compiled language like Go for Textio is that when we deploy our server we don't need to include any runtime language dependencies like Node or a Python interpreter. We just add the pre-compiled binary to the server and start it up!

View File

@@ -0,0 +1,9 @@
{
"question": "Why is it generally more simple to deploy a compiled server program?",
"answers": [
"There are no runtime language dependencies",
"Compiled code is faster",
"Compiled code is more memory efficient",
"Because docker exists"
]
}

View File

@@ -0,0 +1,24 @@
# Compiled vs Interpreted
Compiled programs can be run without access to the original source code, and without access to a compiler. For example, when your browser executes the code you write in this course, it doesn't use the original code, just the compiled result.
Note how this is different than interpreted languages like Python and JavaScript. With Python and JavaScript the code is interpreted at [runtime](https://en.wikipedia.org/wiki/Runtime_(program_lifecycle_phase)) by a separate program known as the "interpreter". Distributing code for users to run can be a pain because they need to have an interpreter installed, and they need access to the original source code.
## Examples of compiled languages
* Go
* C
* C++
* Rust
## Examples of interpreted languages
* JavaScript
* Python
* Ruby
![compiled vs interpreted](https://i.imgur.com/ovHaWmS.jpg)
## Why build Textio in a compiled language?
One of the most convenient things about using a compiled language like Go for Textio is that when we deploy our server we don't need to include any runtime language dependencies like Node or a Python interpreter. We just add the pre-compiled binary to the server and start it up!

View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
var username string = "wagslane"
var password int = 20947382822
// don't edit below this line
fmt.Println("Authorization: Basic", username+":"+password)
}

View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
var username string = "wagslane"
var password string = "20947382822"
// don't edit below this line
fmt.Println("Authorization: Basic", username+":"+password)
}

View File

@@ -0,0 +1 @@
Authorization: Basic wagslane:20947382822

View File

@@ -0,0 +1,17 @@
# Go is Strongly Typed
Go enforces strong and static typing, meaning variables can only have a single type. A `string` variable like "hello world" can not be changed to an `int`, such as the number `3`.
One of the biggest benefits of strong typing is that errors can be caught at "compile time". In other words, bugs are more easily caught ahead of time because they are detected when the code is compiled before it even runs.
Contrast this with most interpreted languages, where the variable types are dynamic. Dynamic typing can lead to subtle bugs that are hard to detect. With interpreted languages, the code *must* be run (sometimes in production if you are unlucky 😨) to catch syntax and type errors.
## Concatenating strings
Two strings can be [concatenated](https://en.wikipedia.org/wiki/Concatenation) with the `+` operator. Because Go is strongly typed, it won't allow you to concatenate a string variable with a numeric variable.
## Assignment
We'll be using simple [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) for the Textio API. This is how our users will communicate to us who they are and how many features they are paying for with each request to our API.
The code on the right has a type error. Change the type of `password` to a string (but use the same numeric value) so that it can be concatenated with the `username` variable.

View File

@@ -0,0 +1,7 @@
{
"question": "Generally speaking, which language uses more memory?",
"answers": [
"Java",
"Go"
]
}

View File

@@ -0,0 +1,17 @@
# Go programs are easy on memory
Go programs are fairly lightweight. Each program includes a small amount of "extra" code that's included in the executable binary. This extra code is called the [Go Runtime](https://go.dev/doc/faq#runtime). One of the purposes of the Go runtime is to cleanup unused memory at runtime.
In other words, the Go compiler includes a small amount of extra logic in every Go program to make it easier for developers to write code that's memory efficient.
## Comparison
As a general rule Java programs use *more* memory than comparable Go programs because Go doesn't use an entire virtual machine to run its programs, just a small runtime. The Go runtime is small enough that it is included directly in each Go program's compiled machine code.
As another general rule Rust and C++ programs use slightly *less* memory than Go programs because more control is given to the developer to optimize memory usage of the program. The Go runtime just handles it for us automatically.
## Idle memory usage
![idle memory](https://miro.medium.com/max/1400/1*Ggs-bJxobwZmrbfuoWGpFw.png)
In the chart above, [Dexter Darwich compares the memory usage](https://medium.com/@dexterdarwich/comparison-between-java-go-and-rust-fdb21bd5fb7c) of three *very* simple programs written in Java, Go, and Rust. As you can see, Go and Rust use *very* little memory when compared to Java.

View File

@@ -0,0 +1,9 @@
{
"question": "What's one of the purposes of the Go runtime?",
"answers": [
"To cleanup unused memory",
"To compile Go code",
"To style Go code and make it easier to read",
"To cook fried chicken"
]
}

View File

@@ -0,0 +1,17 @@
# Go programs are easy on memory
Go programs are fairly lightweight. Each program includes a small amount of "extra" code that's included in the executable binary. This extra code is called the [Go Runtime](https://go.dev/doc/faq#runtime). One of the purposes of the Go runtime is to cleanup unused memory at runtime.
In other words, the Go compiler includes a small amount of extra logic in every Go program to make it easier for developers to write code that's memory efficient.
## Comparison
As a general rule Java programs use *more* memory than comparable Go programs because Go doesn't use an entire virtual machine to run its programs, just a small runtime. The Go runtime is small enough that it is included directly in each Go program's compiled machine code.
As another general rule Rust and C++ programs use slightly *less* memory than Go programs because more control is given to the developer to optimize memory usage of the program. The Go runtime just handles it for us automatically.
## Idle memory usage
![idle memory](https://miro.medium.com/max/1400/1*Ggs-bJxobwZmrbfuoWGpFw.png)
In the chart above, [Dexter Darwich compares the memory usage](https://medium.com/@dexterdarwich/comparison-between-java-go-and-rust-fdb21bd5fb7c) of three *very* simple programs written in Java, Go, and Rust. As you can see, Go and Rust use *very* little memory when compared to Java.

View File

@@ -0,0 +1,49 @@
package main
import "fmt"
func getFormattedMessages(messages []string, formatter func) []string {
formattedMessages := []string{}
for _, message := range messages {
formattedMessages = append(formattedMessages, formatter(message))
}
return formattedMessages
}
// don't touch below this line
func addSignature(message string) string {
return message + " Kind regards."
}
func addGreeting(message string) string {
return "Hello! " + message
}
func test(messages []string, formatter func(string) string) {
defer fmt.Println("====================================")
formattedMessages := getFormattedMessages(messages, formatter)
if len(formattedMessages) != len(messages) {
fmt.Println("The number of messages returned is incorrect.")
return
}
for i, message := range messages {
formatted := formattedMessages[i]
fmt.Printf(" * %s -> %s\n", message, formatted)
}
}
func main() {
test([]string{
"Thanks for getting back to me.",
"Great to see you again.",
"I would love to hang out this weekend.",
"Got any hot stock tips?",
}, addSignature)
test([]string{
"Thanks for getting back to me.",
"Great to see you again.",
"I would love to hang out this weekend.",
"Got any hot stock tips?",
}, addGreeting)
}

View File

@@ -0,0 +1,49 @@
package main
import "fmt"
func getFormattedMessages(messages []string, formatter func(string) string) []string {
formattedMessages := []string{}
for _, message := range messages {
formattedMessages = append(formattedMessages, formatter(message))
}
return formattedMessages
}
// don't touch below this line
func addSignature(message string) string {
return message + " Kind regards."
}
func addGreeting(message string) string {
return "Hello! " + message
}
func test(messages []string, formatter func(string) string) {
defer fmt.Println("====================================")
formattedMessages := getFormattedMessages(messages, formatter)
if len(formattedMessages) != len(messages) {
fmt.Println("The number of messages returned is incorrect.")
return
}
for i, message := range messages {
formatted := formattedMessages[i]
fmt.Printf(" * %s -> %s\n", message, formatted)
}
}
func main() {
test([]string{
"Thanks for getting back to me.",
"Great to see you again.",
"I would love to hang out this weekend.",
"Got any hot stock tips?",
}, addSignature)
test([]string{
"Thanks for getting back to me.",
"Great to see you again.",
"I would love to hang out this weekend.",
"Got any hot stock tips?",
}, addGreeting)
}

View File

@@ -0,0 +1,10 @@
* Thanks for getting back to me. -> Thanks for getting back to me. Kind regards.
* Great to see you again. -> Great to see you again. Kind regards.
* I would love to hang out this weekend. -> I would love to hang out this weekend. Kind regards.
* Got any hot stock tips? -> Got any hot stock tips? Kind regards.
====================================
* Thanks for getting back to me. -> Hello! Thanks for getting back to me.
* Great to see you again. -> Hello! Great to see you again.
* I would love to hang out this weekend. -> Hello! I would love to hang out this weekend.
* Got any hot stock tips? -> Hello! Got any hot stock tips?
====================================

View File

@@ -0,0 +1,37 @@
# First Class and Higher Order Functions
A programming language is said to have "first-class functions" when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
A function that returns a function or accepts a function as input is called a Higher-Order Function.
Go supports [first-class](https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function) and higher-order functions. Another way to think of this is that a function is just another type -- just like `int`s and `string`s and `bool`s.
For example, to accept a function as a parameter:
```go
func add(x, y int) int {
return x + y
}
func mul(x, y int) int {
return x * y
}
// aggregate applies the given math function to the first 3 inputs
func aggregate(a, b, c int, arithmetic func(int, int) int) int {
return arithmetic(arithmetic(a, b), c)
}
func main(){
fmt.Println(aggregate(2,3,4, add))
// prints 9
fmt.Println(aggregate(2,3,4, mul))
// prints 24
}
```
## Assignment
Textio is launching a new email messaging product, "Mailio"!
Fix the compile-time bug in the `getFormattedMessages` function. The function body is correct, but the function signature is not.

View File

@@ -0,0 +1,8 @@
{
"question": "What is a higher-order function?",
"answers": [
"A function that takes another function as an argument",
"A function with superior logic",
"A function that is first in the call stack"
]
}

View File

@@ -0,0 +1,29 @@
# Why First-class and Higher-Order Functions?
At first, it may seem like dynamically creating functions and passing them around as variables adds unnecessary complexity. Most of the time you would be right. There are cases however when functions as values make a lot of sense. Some of these include:
* [HTTP API](https://en.wikipedia.org/wiki/Web_API) handlers
* [Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) handlers
* Onclick callbacks
Any time you need to run custom code at *a time in the future*, functions as values might make sense.
## Definition: First-class Functions
A first-class function is a function that can be treated like any other value. Go supports first-class functions. A function's type is dependent on the types of its parameters and return values. For example, these are different function types:
```go
func() int
```
```go
func(string) int
```
## Definition: Higher-Order Functions
A higher-order function is a function that takes a function as an argument or returns a function as a return value. Go supports higher-order functions. For example, this function takes a function as an argument:
```go
func aggregate(a, b, c int, arithmetic func(int, int) int) int
```

View File

@@ -0,0 +1,8 @@
{
"question": "What is a first-class function?",
"answers": [
"A function that is treated like any other variable",
"A function that has been deemed most important by the architect",
"A function that takes another function as an argument"
]
}

View File

@@ -0,0 +1,29 @@
# Why First-class and Higher-Order Functions?
At first, it may seem like dynamically creating functions and passing them around as variables adds unnecessary complexity. Most of the time you would be right. There are cases however when functions as values make a lot of sense. Some of these include:
* [HTTP API](https://en.wikipedia.org/wiki/Web_API) handlers
* [Pub/Sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) handlers
* Onclick callbacks
Any time you need to run custom code at *a time in the future*, functions as values might make sense.
## Definition: First-class Functions
A first-class function is a function that can be treated like any other value. Go supports first-class functions. A function's type is dependent on the types of its parameters and return values. For example, these are different function types:
```go
func() int
```
```go
func(string) int
```
## Definition: Higher-Order Functions
A higher-order function is a function that takes a function as an argument or returns a function as a return value. Go supports higher-order functions. For example, this function takes a function as an argument:
```go
func aggregate(a, b, c int, arithmetic func(int, int) int) int
```

View File

@@ -0,0 +1,47 @@
package main
import (
"errors"
"fmt"
)
// getLogger takes a function that formats two strings into
// a single string and returns a function that formats two strings but prints
// the result instead of returning it
func getLogger(formatter func(string, string) string) func(string, string) {
// ?
}
// don't touch below this line
func test(first string, errors []error, formatter func(string, string) string) {
defer fmt.Println("====================================")
logger := getLogger(formatter)
fmt.Println("Logs:")
for _, err := range errors {
logger(first, err.Error())
}
}
func colonDelimit(first, second string) string {
return first + ": " + second
}
func commaDelimit(first, second string) string {
return first + ", " + second
}
func main() {
dbErrors := []error{
errors.New("out of memory"),
errors.New("cpu is pegged"),
errors.New("networking issue"),
errors.New("invalid syntax"),
}
test("Error on database server", dbErrors, colonDelimit)
mailErrors := []error{
errors.New("email too large"),
errors.New("non alphanumeric symbols found"),
}
test("Error on mail server", mailErrors, commaDelimit)
}

View File

@@ -0,0 +1,49 @@
package main
import (
"errors"
"fmt"
)
// getLogger takes a function that formats two strings into
// a single string and returns a function that formats two strings but prints
// the result instead of returning it
func getLogger(formatter func(string, string) string) func(string, string) {
return func(first, second string) {
fmt.Println(formatter(first, second))
}
}
// don't touch below this line
func test(first string, errors []error, formatter func(string, string) string) {
defer fmt.Println("====================================")
logger := getLogger(formatter)
fmt.Println("Logs:")
for _, err := range errors {
logger(first, err.Error())
}
}
func colonDelimit(first, second string) string {
return first + ": " + second
}
func commaDelimit(first, second string) string {
return first + ", " + second
}
func main() {
dbErrors := []error{
errors.New("out of memory"),
errors.New("cpu is pegged"),
errors.New("networking issue"),
errors.New("invalid syntax"),
}
test("Error on database server", dbErrors, colonDelimit)
mailErrors := []error{
errors.New("email too large"),
errors.New("non alphanumeric symbols found"),
}
test("Error on mail server", mailErrors, commaDelimit)
}

View File

@@ -0,0 +1,10 @@
Logs:
Error on database server: out of memory
Error on database server: cpu is pegged
Error on database server: networking issue
Error on database server: invalid syntax
====================================
Logs:
Error on mail server, email too large
Error on mail server, non alphanumeric symbols found
====================================

View File

@@ -0,0 +1,40 @@
# Currying
Function currying is the practice of writing a function that takes a function (or functions) as input, and returns a new function.
For example:
```go
func main() {
squareFunc := selfMath(multiply)
doubleFunc := selfMath(add)
fmt.Println(squareFunc(5))
// prints 25
fmt.Println(doubleFunc(5))
// prints 10
}
func multiply(x, y int) int {
return x * y
}
func add(x, y int) int {
return x + y
}
func selfMath(mathFunc func(int, int) int) func (int) int {
return func(x int) int {
return mathFunc(x, x)
}
}
```
In the example above, the `selfMath` function takes in a function as its parameter, and returns a function that itself returns the value of running that input function on its parameter.
## Assignment
The Mailio API needs a very robust error-logging system so we can see when things are going awry in the back-end system. We need a function that can create a custom "logger" (a function that prints to the console) given a specific formatter.
Complete the `getLogger` function. It should `return` *a new function* that prints the formatted inputs using the given `formatter` function. The inputs should be passed into the formatter function in the order they are given to the logger function.

View File

@@ -0,0 +1,91 @@
package main
import (
"fmt"
"sort"
)
const (
logDeleted = "user deleted"
logNotFound = "user not found"
logAdmin = "admin deleted"
)
func logAndDelete(users map[string]user, name string) (log string) {
user, ok := users[name]
if !ok {
delete(users, name)
return logNotFound
}
if user.admin {
return logAdmin
}
delete(users, name)
return logDeleted
}
// don't touch below this line
type user struct {
name string
number int
admin bool
}
func test(users map[string]user, name string) {
fmt.Printf("Attempting to delete %s...\n", name)
defer fmt.Println("====================================")
log := logAndDelete(users, name)
fmt.Println("Log:", log)
}
func main() {
users := map[string]user{
"john": {
name: "john",
number: 18965554631,
admin: true,
},
"elon": {
name: "elon",
number: 19875556452,
admin: true,
},
"breanna": {
name: "breanna",
number: 98575554231,
admin: false,
},
"kade": {
name: "kade",
number: 10765557221,
admin: false,
},
}
fmt.Println("Initial users:")
usersSorted := []string{}
for name := range users {
usersSorted = append(usersSorted, name)
}
sort.Strings(usersSorted)
for _, name := range usersSorted {
fmt.Println(" -", name)
}
fmt.Println("====================================")
test(users, "john")
test(users, "santa")
test(users, "kade")
fmt.Println("Final users:")
usersSorted = []string{}
for name := range users {
usersSorted = append(usersSorted, name)
}
sort.Strings(usersSorted)
for _, name := range usersSorted {
fmt.Println(" -", name)
}
fmt.Println("====================================")
}

View File

@@ -0,0 +1,91 @@
package main
import (
"fmt"
"sort"
)
const (
logDeleted = "user deleted"
logNotFound = "user not found"
logAdmin = "admin deleted"
)
func logAndDelete(users map[string]user, name string) (log string) {
defer delete(users, name)
user, ok := users[name]
if !ok {
return logNotFound
}
if user.admin {
return logAdmin
}
return logDeleted
}
// don't touch below this line
type user struct {
name string
number int
admin bool
}
func test(users map[string]user, name string) {
fmt.Printf("Attempting to delete %s...\n", name)
defer fmt.Println("====================================")
log := logAndDelete(users, name)
fmt.Println("Log:", log)
}
func main() {
users := map[string]user{
"john": {
name: "john",
number: 18965554631,
admin: true,
},
"elon": {
name: "elon",
number: 19875556452,
admin: true,
},
"breanna": {
name: "breanna",
number: 98575554231,
admin: false,
},
"kade": {
name: "kade",
number: 10765557221,
admin: false,
},
}
fmt.Println("Initial users:")
usersSorted := []string{}
for name := range users {
usersSorted = append(usersSorted, name)
}
sort.Strings(usersSorted)
for _, name := range usersSorted {
fmt.Println(" -", name)
}
fmt.Println("====================================")
test(users, "john")
test(users, "santa")
test(users, "kade")
fmt.Println("Final users:")
usersSorted = []string{}
for name := range users {
usersSorted = append(usersSorted, name)
}
sort.Strings(usersSorted)
for _, name := range usersSorted {
fmt.Println(" -", name)
}
fmt.Println("====================================")
}

View File

@@ -0,0 +1,19 @@
Initial users:
- breanna
- elon
- john
- kade
====================================
Attempting to delete john...
Log: admin deleted
====================================
Attempting to delete santa...
Log: user not found
====================================
Attempting to delete kade...
Log: user deleted
====================================
Final users:
- breanna
- elon
====================================

View File

@@ -0,0 +1,46 @@
# Defer
The `defer` keyword is a fairly unique feature of Go. It allows a function to be executed automatically *just before* its enclosing function returns.
The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
Deferred functions are typically used to close database connections, file handlers and the like.
For example:
```go
// CopyFile copies a file from srcName to dstName on the local filesystem.
func CopyFile(dstName, srcName string) (written int64, err error) {
// Open the source file
src, err := os.Open(srcName)
if err != nil {
return
}
// Close the source file when the CopyFile function returns
defer src.Close()
// Create the destination file
dst, err := os.Create(dstName)
if err != nil {
return
}
// Close the destination file when the CopyFile function returns
defer dst.Close()
return io.Copy(dst, src)
}
```
In the above example, the `src.Close()` function is not called until after the `CopyFile` function was called but immediately before the `CopyFile` function returns.
Defer is a great way to **make sure** that something happens at the end of a function, even if there are multiple return statements.
## Assignment
There is a bug in the `logAndDelete` function, fix it!
This function should *always* delete the user from the user's map, which is a map that stores the user's name as keys. It also returns a `log` string that indicates to the caller some information about the user's deletion.
To avoid bugs like this in the future, instead of calling `delete` before each `return`, just `defer` the delete once at the beginning of the function.

View File

@@ -0,0 +1,46 @@
package main
import "fmt"
func adder() func(int) int {
// ?
}
// don't touch below this line
type emailBill struct {
costInPennies int
}
func test(bills []emailBill) {
defer fmt.Println("====================================")
countAdder, costAdder := adder(), adder()
for _, bill := range bills {
fmt.Printf("You've sent %d emails and it has cost you %d cents\n", countAdder(1), costAdder(bill.costInPennies))
}
}
func main() {
test([]emailBill{
{45},
{32},
{43},
{12},
{34},
{54},
})
test([]emailBill{
{12},
{12},
{976},
{12},
{543},
})
test([]emailBill{
{743},
{13},
{8},
})
}

View File

@@ -0,0 +1,50 @@
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
// don't touch below this line
type emailBill struct {
costInPennies int
}
func test(bills []emailBill) {
defer fmt.Println("====================================")
countAdder, costAdder := adder(), adder()
for _, bill := range bills {
fmt.Printf("You've sent %d emails and it has cost you %d cents\n", countAdder(1), costAdder(bill.costInPennies))
}
}
func main() {
test([]emailBill{
{45},
{32},
{43},
{12},
{34},
{54},
})
test([]emailBill{
{12},
{12},
{976},
{12},
{543},
})
test([]emailBill{
{743},
{13},
{8},
})
}

View File

@@ -0,0 +1,17 @@
You've sent 1 emails and it has cost you 45 cents
You've sent 2 emails and it has cost you 77 cents
You've sent 3 emails and it has cost you 120 cents
You've sent 4 emails and it has cost you 132 cents
You've sent 5 emails and it has cost you 166 cents
You've sent 6 emails and it has cost you 220 cents
====================================
You've sent 1 emails and it has cost you 12 cents
You've sent 2 emails and it has cost you 24 cents
You've sent 3 emails and it has cost you 1000 cents
You've sent 4 emails and it has cost you 1012 cents
You've sent 5 emails and it has cost you 1555 cents
====================================
You've sent 1 emails and it has cost you 743 cents
You've sent 2 emails and it has cost you 756 cents
You've sent 3 emails and it has cost you 764 cents
====================================

View File

@@ -0,0 +1,37 @@
# Closures
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
In this example, the `concatter()` function returns a function that has reference to an *enclosed* `doc` value. Each successive call to `harryPotterAggregator` mutates that same `doc` variable.
```go
func concatter() func(string) string {
doc := ""
return func(word string) string {
doc += word + " "
return doc
}
}
func main() {
harryPotterAggregator := concatter()
harryPotterAggregator("Mr.")
harryPotterAggregator("and")
harryPotterAggregator("Mrs.")
harryPotterAggregator("Dursley")
harryPotterAggregator("of")
harryPotterAggregator("number")
harryPotterAggregator("four,")
harryPotterAggregator("Privet")
fmt.Println(harryPotterAggregator("Drive"))
// Mr. and Mrs. Dursley of number four, Privet Drive
}
```
## Assignment
Keeping track of how many emails we send is mission-critical at Mailio. Complete the `adder()` function.
It should return a function that adds its input (an `int`) to an enclosed `sum` value, then return the new sum. In other words, it keeps a running total of the `sum` variable within a closure.

View File

@@ -0,0 +1,7 @@
{
"question": "Can a closure mutate a variable outside its body?",
"answers": [
"Yes",
"No"
]
}

View File

@@ -0,0 +1,30 @@
# Closure Review
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
## Example Closure
```go
func concatter() func(string) string {
doc := ""
return func(word string) string {
doc += word + " "
return doc
}
}
func main() {
harryPotterAggregator := concatter()
harryPotterAggregator("Mr.")
harryPotterAggregator("and")
harryPotterAggregator("Mrs.")
harryPotterAggregator("Dursley")
harryPotterAggregator("of")
harryPotterAggregator("number")
harryPotterAggregator("four,")
harryPotterAggregator("Privet")
fmt.Println(harryPotterAggregator("Drive"))
// Mr. and Mrs. Dursley of number four, Privet Drive
}
```

View File

@@ -0,0 +1,7 @@
{
"question": "When a variable is enclosed in a closure, the enclosing function has access to ____",
"answers": [
"a mutable reference to the original value",
"a copy of the value"
]
}

View File

@@ -0,0 +1,30 @@
# Closure Review
A closure is a function that references variables from outside its own function body. The function may access and *assign* to the referenced variables.
## Example Closure
```go
func concatter() func(string) string {
doc := ""
return func(word string) string {
doc += word + " "
return doc
}
}
func main() {
harryPotterAggregator := concatter()
harryPotterAggregator("Mr.")
harryPotterAggregator("and")
harryPotterAggregator("Mrs.")
harryPotterAggregator("Dursley")
harryPotterAggregator("of")
harryPotterAggregator("number")
harryPotterAggregator("four,")
harryPotterAggregator("Privet")
fmt.Println(harryPotterAggregator("Drive"))
// Mr. and Mrs. Dursley of number four, Privet Drive
}
```

View File

@@ -0,0 +1,36 @@
package main
import "fmt"
func printReports(messages []string) {
// ?
}
// don't touch below this line
func test(messages []string) {
defer fmt.Println("====================================")
printReports(messages)
}
func main() {
test([]string{
"Here's Johnny!",
"Go ahead, make my day",
"You had me at hello",
"There's no place like home",
})
test([]string{
"Hello, my name is Inigo Montoya. You killed my father. Prepare to die.",
"May the Force be with you.",
"Show me the money!",
"Go ahead, make my day.",
})
}
func printCostReport(costCalculator func(string) int, message string) {
cost := costCalculator(message)
fmt.Printf(`Message: "%s" Cost: %v cents`, message, cost)
fmt.Println()
}

View File

@@ -0,0 +1,40 @@
package main
import "fmt"
func printReports(messages []string) {
for _, message := range messages {
printCostReport(func(m string) int {
return len(m) * 2
}, message)
}
}
// don't touch below this line
func test(messages []string) {
defer fmt.Println("====================================")
printReports(messages)
}
func main() {
test([]string{
"Here's Johnny!",
"Go ahead, make my day",
"You had me at hello",
"There's no place like home",
})
test([]string{
"Hello, my name is Inigo Montoya. You killed my father. Prepare to die.",
"May the Force be with you.",
"Show me the money!",
"Go ahead, make my day.",
})
}
func printCostReport(costCalculator func(string) int, message string) {
cost := costCalculator(message)
fmt.Printf(`Message: "%s" Cost: %v cents`, message, cost)
fmt.Println()
}

View File

@@ -0,0 +1,10 @@
Message: "Here's Johnny!" Cost: 28 cents
Message: "Go ahead, make my day" Cost: 42 cents
Message: "You had me at hello" Cost: 38 cents
Message: "There's no place like home" Cost: 52 cents
====================================
Message: "Hello, my name is Inigo Montoya. You killed my father. Prepare to die." Cost: 140 cents
Message: "May the Force be with you." Cost: 52 cents
Message: "Show me the money!" Cost: 36 cents
Message: "Go ahead, make my day." Cost: 44 cents
====================================

View File

@@ -0,0 +1,38 @@
# Anonymous Functions
Anonymous functions are true to form in that they have *no name*. We've been using them throughout this chapter, but we haven't really talked about them yet.
Anonymous functions are useful when defining a function that will only be used once or to create a quick [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)).
```go
// doMath accepts a function that converts one int into another
// and a slice of ints. It returns a slice of ints that have been
// converted by the passed in function.
func doMath(f func(int) int, nums []int) []int {
var results []int
for _, n := range nums {
results = append(results, f(n))
}
return results
}
func main() {
nums := []int{1, 2, 3, 4, 5}
// Here we define an anonymous function that doubles an int
// and pass it to doMath
allNumsDoubled := doMath(func(x int) int {
return x + x
}, nums)
fmt.Println(allNumsDoubled)
// prints:
// [2 4 6 8 10]
}
```
## Assignment
Complete the `printReports` function.
Call `printCostReport` once for each message. Pass in an anonymous function as the `costCalculator` that returns an `int` equal to twice the length of the input message.

View File

@@ -0,0 +1,29 @@
package main
import "fmt"
type Message struct {
Recipient string
Text string
}
// Don't touch above this line
func sendMessage(m Message) {
fmt.Printf("To: %v\n", &m.Recipient)
fmt.Printf("Message: %v\n", &m.Text)
}
// Don't touch below this line
func test(recipient string, text string) {
m := Message{Recipient: recipient, Text: text}
sendMessage(m)
fmt.Println("=====================================")
}
func main() {
test("Lane", "Textio is getting better everyday!")
test("Allan", "This pointer stuff is weird...")
test("Tiffany", "What time will you be home for dinner?")
}

View File

@@ -0,0 +1,29 @@
package main
import "fmt"
type Message struct {
Recipient string
Text string
}
// Don't touch above this line
func sendMessage(m Message) {
fmt.Printf("To: %v\n", m.Recipient)
fmt.Printf("Message: %v\n", m.Text)
}
// Don't touch below this line
func test(recipient string, text string) {
m := Message{Recipient: recipient, Text: text}
sendMessage(m)
fmt.Println("=====================================")
}
func main() {
test("Lane", "Textio is getting better everyday!")
test("Allan", "This pointer stuff is weird...")
test("Tiffany", "What time will you be home for dinner?")
}

View File

@@ -0,0 +1,9 @@
To: Lane
Message: Textio is getting better everyday!
=====================================
To: Allan
Message: This pointer stuff is weird...
=====================================
To: Tiffany
Message: What time will you be home for dinner?
=====================================

View File

@@ -0,0 +1,33 @@
# Introduction to Pointers
As we have learned, a variable is a named location in memory that stores a value. We can manipulate the value of a variable by assigning a new value to it or by performing operations on it. When we assign a value to a variable, we are storing that value in a specific location in memory.
```go
x := 42
// "x" is the name of a location in memory. That location is storing the integer value of 42
```
## A pointer is a variable
A pointer is a variable that stores the *memory address* of another variable. This means that a pointer "points to" the *location* of where the data is stored *NOT* the actual data itself.
The `*` syntax defines a pointer:
```go
var p *int
```
The `&` operator generates a pointer to its operand.
```go
myString := "hello"
myStringPtr = &myString
```
## Why are pointers useful?
Pointers allow us to manipulate data in memory directly, without making copies or duplicating data. This can make programs more efficient and allow us to do things that would be difficult or impossible without them.
## Assignment
Fix the bug in the `sendMessage` function. It's *supposed* to print a nicely formatted message to the console containing an SMS's recipient and message body. However, it's not working as expected. Run the code and see what happens, then fix the bug.

View File

@@ -0,0 +1,35 @@
package main
import (
"fmt"
)
func removeProfanity(message *string) {
// ?
}
// don't touch below this line
func test(messages []string) {
for _, message := range messages {
removeProfanity(&message)
fmt.Println(message)
}
}
func main() {
messages1 := []string{
"well shoot, this is awful",
"dang robots",
"dang them to heck",
}
messages2 := []string{
"well shoot",
"Allan is going straight to heck",
"dang... that's a tough break",
}
test(messages1)
test(messages2)
}

View File

@@ -0,0 +1,40 @@
package main
import (
"fmt"
"strings"
)
func removeProfanity(message *string) {
messageVal := *message
messageVal = strings.ReplaceAll(messageVal, "dang", "****")
messageVal = strings.ReplaceAll(messageVal, "shoot", "*****")
messageVal = strings.ReplaceAll(messageVal, "heck", "****")
*message = messageVal
}
// don't touch below this line
func test(messages []string) {
for _, message := range messages {
removeProfanity(&message)
fmt.Println(message)
}
}
func main() {
messages1 := []string{
"well shoot, this is awful",
"dang robots",
"dang them to heck",
}
messages2 := []string{
"well shoot",
"Allan is going straight to heck",
"dang... that's a tough break",
}
test(messages1)
test(messages2)
}

View File

@@ -0,0 +1,6 @@
well *****, this is awful
**** robots
**** them to ****
well *****
Allan is going straight to ****
****... that's a tough break

View File

@@ -0,0 +1,45 @@
# Pointers
Pointers hold the memory address of a value.
The `*` syntax defines a pointer:
```go
var p *int
```
A pointer's zero value is `nil`
The & operator generates a pointer to its operand.
```go
myString := "hello"
myStringPtr = &myString
```
The * dereferences a pointer to gain access to the value
```go
fmt.Println(*myStringPtr) // read myString through the pointer
*myStringPtr = "world" // set myString through the pointer
```
Unlike C, Go has no [pointer arithmetic](https://www.tutorialspoint.com/cprogramming/c_pointer_arithmetic.htm)
## Just because you can doesn't mean you should
We're doing this exercise to understand that pointers **can** be used in this way. That said, pointers can be *very* dangerous. It's generally a better idea to have your functions accept non-pointers and return new values rather than mutating pointer inputs.
## Assignment
Complete the `removeProfanity` function.
It should use the [strings.ReplaceAll](https://pkg.go.dev/strings#ReplaceAll) function to replace all instances of the following words in the input `message` with asterisks.
* "dang" -> "****"
* "shoot" -> "*****"
* "heck" -> "****"
It should *mutate* the value in the pointer and return nothing.
Do *not* alter the function signature.

View File

@@ -0,0 +1,9 @@
{
"question": "What is the value of *y after the code on the left executes?",
"answers": [
"100",
"a memory address pointing to x",
"nil",
"50"
]
}

View File

@@ -0,0 +1,13 @@
# Pointers Quiz
```go
package main
import "fmt"
func main() {
var x int = 50
var y *int = &x
*y = 100
}
```

View File

@@ -0,0 +1,9 @@
{
"question": "What is the value of x after the code on the left executes?",
"answers": [
"100",
"a memory address pointing to x",
"50",
"nil"
]
}

View File

@@ -0,0 +1,13 @@
# Pointers Quiz
```go
package main
import "fmt"
func main() {
var x int = 50
var y *int = &x
*y = 100
}
```

View File

@@ -0,0 +1,51 @@
package main
import (
"fmt"
"strings"
)
func removeProfanity(message *string) {
// ?
messageVal := *message
messageVal = strings.ReplaceAll(messageVal, "dang", "****")
messageVal = strings.ReplaceAll(messageVal, "shoot", "*****")
messageVal = strings.ReplaceAll(messageVal, "heck", "****")
*message = messageVal
}
// don't touch below this line
func test(messages []string) {
for _, message := range messages {
if message == "" {
removeProfanity(nil)
fmt.Println("nil message detected")
} else {
removeProfanity(&message)
fmt.Println(message)
}
}
}
func main() {
messages := []string{
"well shoot, this is awful",
"",
"dang robots",
"dang them to heck",
"",
}
messages2 := []string{
"well shoot",
"",
"Allan is going straight to heck",
"dang... that's a tough break",
"",
}
test(messages)
test(messages2)
}

View File

@@ -0,0 +1,53 @@
package main
import (
"fmt"
"strings"
)
func removeProfanity(message *string) {
if message == nil {
return
}
messageVal := *message
messageVal = strings.ReplaceAll(messageVal, "dang", "****")
messageVal = strings.ReplaceAll(messageVal, "shoot", "*****")
messageVal = strings.ReplaceAll(messageVal, "heck", "****")
*message = messageVal
}
// don't touch below this line
func test(messages []string) {
for _, message := range messages {
if message == "" {
removeProfanity(nil)
fmt.Println("nil message detected")
} else {
removeProfanity(&message)
fmt.Println(message)
}
}
}
func main() {
messages := []string{
"well shoot, this is awful",
"",
"dang robots",
"dang them to heck",
"",
}
messages2 := []string{
"well shoot",
"",
"Allan is going straight to heck",
"dang... that's a tough break",
"",
}
test(messages)
test(messages2)
}

View File

@@ -0,0 +1,10 @@
well *****, this is awful
nil message detected
**** robots
**** them to ****
nil message detected
well *****
nil message detected
Allan is going straight to ****
****... that's a tough break
nil message detected

View File

@@ -0,0 +1,9 @@
# Nil Pointers
Pointers can be very dangerous.
If a pointer points to nothing (the zero value of the pointer type) then dereferencing it will cause a runtime error (a [panic](https://gobyexample.com/panic)) that crashes the program. Generally speaking, whenever you're dealing with pointers you should check if it's `nil` before trying to dereference it.
## Assignment
Let's make our profanity checker *safe*. Update the `removeProfanity` function. If `message` is `nil`, `return` early to avoid a [panic](https://gobyexample.com/panic). After all, there are no bad words to remove.

View File

@@ -0,0 +1,7 @@
{
"question": "Which is more widely used in Go?",
"answers": [
"Pointer receivers",
"Value receivers"
]
}

View File

@@ -0,0 +1,47 @@
# Pointer Receivers
A receiver type on a method can be a pointer.
Methods with pointer receivers can modify the value to which the receiver points. Since methods often need to modify their receiver, pointer receivers are *more common* than value receivers.
## Pointer receiver
```go
type car struct {
color string
}
func (c *car) setColor(color string) {
c.color = color
}
func main() {
c := car{
color: "white",
}
c.setColor("blue")
fmt.Println(c.color)
// prints "blue"
}
```
## Non-pointer receiver
```go
type car struct {
color string
}
func (c car) setColor(color string) {
c.color = color
}
func main() {
c := car{
color: "white",
}
c.setColor("blue")
fmt.Println(c.color)
// prints "white"
}
```

View File

@@ -0,0 +1,49 @@
package main
import (
"fmt"
)
func (e email) setMessage(newMessage string) {
e.message = newMessage
}
// don't edit below this line
type email struct {
message string
fromAddress string
toAddress string
}
func test(e *email, newMessage string) {
fmt.Println("-- before --")
e.print()
fmt.Println("-- end before --")
e.setMessage("this is my second draft")
fmt.Println("-- after --")
e.print()
fmt.Println("-- end after --")
fmt.Println("==========================")
}
func (e email) print() {
fmt.Println("message:", e.message)
fmt.Println("fromAddress:", e.fromAddress)
fmt.Println("toAddress:", e.toAddress)
}
func main() {
test(&email{
message: "this is my first draft",
fromAddress: "sandra@mailio-test.com",
toAddress: "bullock@mailio-test.com",
}, "this is my second draft")
test(&email{
message: "this is my third draft",
fromAddress: "sandra@mailio-test.com",
toAddress: "bullock@mailio-test.com",
}, "this is my fourth draft")
}

View File

@@ -0,0 +1,49 @@
package main
import (
"fmt"
)
func (e *email) setMessage(newMessage string) {
e.message = newMessage
}
// don't edit below this line
type email struct {
message string
fromAddress string
toAddress string
}
func test(e *email, newMessage string) {
fmt.Println("-- before --")
e.print()
fmt.Println("-- end before --")
e.setMessage("this is my second draft")
fmt.Println("-- after --")
e.print()
fmt.Println("-- end after --")
fmt.Println("==========================")
}
func (e email) print() {
fmt.Println("message:", e.message)
fmt.Println("fromAddress:", e.fromAddress)
fmt.Println("toAddress:", e.toAddress)
}
func main() {
test(&email{
message: "this is my first draft",
fromAddress: "sandra@mailio-test.com",
toAddress: "bullock@mailio-test.com",
}, "this is my second draft")
test(&email{
message: "this is my third draft",
fromAddress: "sandra@mailio-test.com",
toAddress: "bullock@mailio-test.com",
}, "this is my fourth draft")
}

View File

@@ -0,0 +1,22 @@
-- before --
message: this is my first draft
fromAddress: sandra@mailio-test.com
toAddress: bullock@mailio-test.com
-- end before --
-- after --
message: this is my second draft
fromAddress: sandra@mailio-test.com
toAddress: bullock@mailio-test.com
-- end after --
==========================
-- before --
message: this is my third draft
fromAddress: sandra@mailio-test.com
toAddress: bullock@mailio-test.com
-- end before --
-- after --
message: this is my second draft
fromAddress: sandra@mailio-test.com
toAddress: bullock@mailio-test.com
-- end after --
==========================

View File

@@ -0,0 +1,33 @@
# Pointer Receiver Code
Methods with pointer receivers don't require that a pointer is used to call the method. The pointer will automatically be derived from the value.
```go
type circle struct {
x int
y int
radius int
}
func (c *circle) grow(){
c.radius *= 2
}
func main(){
c := circle{
x: 1,
y: 2,
radius: 4,
}
// notice c is not a pointer in the calling function
// but the method still gains access to a pointer to c
c.grow()
fmt.Println(c.radius)
// prints 8
}
```
## Assignment
Fix the bug in the code so that `setMessage` sets the `message` field of the given email structure, and the new value persists outside the scope of the `setMessage` method.

View File

@@ -0,0 +1,14 @@
package mailio
import (
"fmt"
)
func test(text string) {
fmt.Println(text)
}
func main() {
test("starting Mailio server")
test("stopping Mailio server")
}

View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
)
func test(text string) {
fmt.Println(text)
}
func main() {
test("starting Mailio server")
test("stopping Mailio server")
}

View File

@@ -0,0 +1,2 @@
starting Mailio server
stopping Mailio server

View File

@@ -0,0 +1,28 @@
# Packages
Every Go program is made up of packages.
You have probably noticed the `package main` at the top of all the programs you have been writing.
A package named "main" has an entrypoint at the `main()` function. A `main` package is compiled into an executable program.
A package by any other name is a "library package". Libraries have no entry point. Libraries simply export functionality that can be used by other packages. For example:
```go
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
```
This program is an executable. It is a "main" package and *imports* from the `fmt` and `math/rand` library packages.
## Assignment
Fix the bug in the code.

View File

@@ -0,0 +1,8 @@
{
"question": "What does 'go install' do?",
"answers": [
"Compiles and installs the program locally",
"Installs dependencies",
"Saves local code to the remote source control provider"
]
}

View File

@@ -0,0 +1,27 @@
# Go Install
## Build an executable
Ensure you are in your `hellogo` repo, then run:
```bash
go install
```
Navigate out of your project directory:
```bash
cd ../
```
Go has installed the `hellogo` program globally. Run it with:
```bash
hellogo
```
## Tip about "not found"
If you get an error regarding "hellogo not found" it means you probably don't have your Go environment setup properly. Specifically, `go install` is adding your binary to your `GOBIN` directory, but that may not be in your `PATH`.
You can read more about that here in the [go install docs](https://pkg.go.dev/cmd/go#hdr-Compile_and_install_packages_and_dependencies).

View File

@@ -0,0 +1,7 @@
{
"question": "Code must be compiled with 'go build' before running 'go install'",
"answers": [
"False",
"True"
]
}

View File

@@ -0,0 +1,27 @@
# Go Install
## Build an executable
Ensure you are in your `hellogo` repo, then run:
```bash
go install
```
Navigate out of your project directory:
```bash
cd ../
```
Go has installed the `hellogo` program globally. Run it with:
```bash
hellogo
```
## Tip about "not found"
If you get an error regarding "hellogo not found" it means you probably don't have your Go environment setup properly. Specifically, `go install` is adding your binary to your `GOBIN` directory, but that may not be in your `PATH`.
You can read more about that here in the [go install docs](https://pkg.go.dev/cmd/go#hdr-Compile_and_install_packages_and_dependencies).

View File

@@ -0,0 +1,7 @@
{
"question": "What was the output from 'go build' in the library package",
"answers": [
"The compiled package is silently saved to the local build cache",
"An executable program"
]
}

View File

@@ -0,0 +1,45 @@
# Custom Package
Let's write a package to import and use in `hellogo`.
Create a sibling directory at the same level as the `hellogo` directory:
```bash
mkdir mystrings
cd mystrings
```
Initialize a module:
```bash
go mod init {REMOTE}/{USERNAME}/mystrings
```
Then create a new file `mystrings.go` in that directory and paste the following code:
```go
// by convention, we name our package the same as the directory
package mystrings
// Reverse reverses a string left to right
// Notice that we need to capitalize the first letter of the function
// If we don't then we won't be able access this function outside of the
// mystrings package
func Reverse(s string) string {
result := ""
for _, v := range s {
result = string(v) + result
}
return result
}
```
Note that there is no `main.go` or `func main()` in this package.
`go build` won't build an executable from a library package. However, `go build` will still compile the package and save it to our local build cache. It's useful for checking for compile errors.
Run:
```bash
go build
```

View File

@@ -0,0 +1,7 @@
{
"question": "Why is the function 'Reverse()' instead of 'reverse()'?",
"answers": [
"Lowercase names aren't exported for external use",
"Conventionally uppercase names are used in Go"
]
}

View File

@@ -0,0 +1,45 @@
# Custom Package
Let's write a package to import and use in `hellogo`.
Create a sibling directory at the same level as the `hellogo` directory:
```bash
mkdir mystrings
cd mystrings
```
Initialize a module:
```bash
go mod init {REMOTE}/{USERNAME}/mystrings
```
Then create a new file `mystrings.go` in that directory and paste the following code:
```go
// by convention, we name our package the same as the directory
package mystrings
// Reverse reverses a string left to right
// Notice that we need to capitlize the first letter of the function
// If we don't then we won't be able access this function outside of the
// mystrings package
func Reverse(s string) string {
result := ""
for _, v := range s {
result = string(v) + result
}
return result
}
```
Note that there is no `main.go` or `func main()` in this package.
`go build` won't build an executable from a library package. However, `go build` will still compile the package and save it to our local build cache. It's useful for checking for compile errors.
Run:
```bash
go build
```

View File

@@ -0,0 +1,8 @@
{
"question": "Does a package in a folder named 'dateparser' need to also be called 'dateparser'",
"answers": [
"No, but it should by convention",
"Yes",
"No, by convention we should avoid consistent naming"
]
}

View File

@@ -0,0 +1,45 @@
# Custom Package
Let's write a package to import and use in `hellogo`.
Create a sibling directory at the same level as the `hellogo` directory:
```bash
mkdir mystrings
cd mystrings
```
Initialize a module:
```bash
go mod init {REMOTE}/{USERNAME}/mystrings
```
Then create a new file `mystrings.go` in that directory and paste the following code:
```go
// by convention, we name our package the same as the directory
package mystrings
// Reverse reverses a string left to right
// Notice that we need to capitlize the first letter of the function
// If we don't then we won't be able access this function outside of the
// mystrings package
func Reverse(s string) string {
result := ""
for _, v := range s {
result = string(v) + result
}
return result
}
```
Note that there is no `main.go` or `func main()` in this package.
`go build` won't build an executable from a library package. However, `go build` will still compile the package and save it to our local build cache. It's useful for checking for compile errors.
Run:
```bash
go build
```

View File

@@ -0,0 +1,8 @@
{
"question": "What was printed by the new hellogo program?",
"answers": [
"dlrow olleh",
"hello world",
"world hello"
]
}

View File

@@ -0,0 +1,40 @@
# Custom Package Continued
Let's use our new `mystrings` package in `hellogo`
Modify hellogo's `main.go` file:
```go
package main
import (
"fmt"
"{REMOTE}/{USERNAME}/mystrings"
)
func main() {
fmt.Println(mystrings.Reverse("hello world"))
}
```
Don't forget to replace {REMOTE} and {USERNAME} with the values you used before. Then edit hellogo's `go.mod` file to contain the following:
```go
module example.com/username/hellogo
go 1.20
replace example.com/username/mystrings v0.0.0 => ../mystrings
require (
example.com/username/mystrings v0.0.0
)
```
Now build and run the new program:
```bash
go build
./hellogo
```

View File

@@ -0,0 +1,9 @@
{
"question": "How does the go toolchain know where to find the imported code?",
"answers": [
"We used the 'replace' keyword in go.mod to point it to the relative location of mystrings",
"It downloads it from Google's servers",
"NPM hosts the files publicly",
"It was fetched from Github"
]
}

Some files were not shown because too many files have changed in this diff Show More