├── 01_setup ├── exercise_0.md └── exercise_1a.md ├── 02_introduction ├── anatomy.md ├── code │ └── exercise_2a_solution.go ├── exercise_2a.md └── printing.md ├── 03_basic_syntax ├── code │ ├── exercise_3a_solution.go │ ├── exercise_3b_solution.go │ ├── for.go │ ├── ifs.go │ ├── scan.go │ └── switch.go ├── ctrl_structures.md ├── exercise_3a.md ├── exercise_3b.md ├── types.md └── variables.md ├── 04_complex_structures ├── arrays.md ├── code │ ├── exercise_4a_solution.go │ ├── exercise_4b_solution.go │ ├── exercise_4c_solution.go │ ├── functions.go │ ├── maps.go │ └── slices.go ├── exercise_4a.md ├── exercise_4b.md ├── exercise_4c.md ├── functions.md ├── maps.md └── slices.md ├── 05_toolkit ├── code │ ├── exercise_5a_solution │ │ ├── packages.go │ │ └── utils │ │ │ ├── add_test.go │ │ │ ├── math.go │ │ │ ├── strings.go │ │ │ └── strings_test.go │ ├── packages.go │ └── tools.go ├── exercise_5a.md ├── packages.md ├── testing.md └── tools.md ├── 06_structs ├── code │ ├── exercise_6a_solution.go │ └── structs.go ├── exercise_6a.md └── structs.md ├── 07_pointers ├── code │ ├── exercise_7a_solution.go │ └── pointers.go ├── exercise_7a.md └── pointers.md ├── 08_errors ├── code │ └── errors.go ├── errors.md └── panic.md ├── 09_methods ├── code │ ├── exercise_9a_solution.go │ └── methods.go ├── exercise_9a.md └── methods.md ├── 10_interfaces ├── code │ └── interfaces.go └── interfaces.md ├── 11_server ├── code │ ├── server.go │ └── server_final.go ├── exercise_11a.md └── server.md ├── 12_api ├── code │ ├── api.go │ └── api_final.go └── exercise_12a.md ├── 13_concurrency ├── code │ ├── concurrency.go │ └── concurrency_final.go └── concurrency.md ├── 14_future_go ├── code │ ├── errors.go │ └── errors_final.go └── future.md ├── README.md ├── main.go └── todos.html /01_setup/exercise_0.md: -------------------------------------------------------------------------------- 1 | # Exercise 0: Installing Go 2 | 3 | ## Goals: 4 | - Install Go on your machine 5 | 6 | ## Setup 7 | - Visit [golang.org/dl](https://golang.org/dl) 8 | 9 | - If you're installing Go for the first time, start at the section `Installing Go From Scratch`. 10 | 11 | - If you already have Go installed, feel free to skip installation or find the section below called `Upgrading To Go 1.13`. 12 | 13 | ## Directions 14 | 15 | ### Installing Go From Scratch 16 | 17 | #### Download Go 18 | 1. Visit [golang.org/dl](https://golang.org/dl) 19 | 2. Select the download package for your operating system 20 | 3. Once the installation package is downloaded, double click on that package to kick off installation. 21 | 22 | #### Verify Installation 23 | 1. When the installation wizard is complete, open up a terminal window and verify Go is installed with the following commands: 24 | 25 | ```bash 26 | which go 27 | => /usr/local/go 28 | 29 | go version 30 | => go version go1.13.1 darwi/amd64 31 | ``` 32 | 33 | #### Modify Your PATH 34 | 35 | 1. Go checks for a certain environment variable, `GOPATH` when executing a Go program. We need to update our bash_profile to help Go find where our files will live. 36 | 37 | 2. In a code editor, open up your `.bash_profile` which should be located in your home directory. 38 | 39 | 3. Add the following lines to the bottom of the file (or wherever you export your PATH environment variables) 40 | 41 | ```bash 42 | export GOPATH=$HOME/go 43 | export GOBIN=$GOPATH/bin 44 | export PATH=$PATH:$GOBIN 45 | ``` 46 | 47 | #### Verify These Updates 48 | 49 | 1. Restart your terminal 50 | 51 | 2. Run the following commands and verify you see similar output 52 | 53 | ```bash 54 | echo $PATH 55 | => ..../usr/local/go/bin: 56 | 57 | echo $GOPATH 58 | => /Users/brennamartenson/go 59 | ``` 60 | 61 | #### Create a go workspace 62 | 63 | We just told Go that whenever we run a Go command in our terminal (ie: `go run main.go`), it should look for those files in the GOPATH, which typically defaults to `/Users/yourusername/go`. 64 | 65 | 1. Navigate to your home directory (for me that is `/Users/brennamartenson`), and create a `go` directory with `mkdir go`. 66 | 67 | 2. Change into the go directory with `cd go`, then `mkdir src`, and change into the directory with `cd src`. 68 | 69 | 3. Clone the repo for this course into the `go` directory. 70 | 71 | `git clone https://github.com/martensonbj/fem-intro-to-go` 72 | 73 | #### Verify Everything Is Good To GO 😉 74 | 75 | 1. Change into the `go/fem-intro-to-go` directory 76 | 77 | 2. Run `go run main.go` 78 | 79 | 3. If all went well, you should see `"Hello Front End Masters!"` 80 | 81 | 82 | ### Upgrading to Go 1.13 83 | 84 | 1. Verify you do have Go installed in the first place: 85 | 86 | ```bash 87 | go version 88 | => go version go1.12.1 darwin/amd64 89 | ``` 90 | 91 | 2. Uninstall Go 92 | 93 | ```bash 94 | sudo rm -rf /usr/local/go 95 | ``` 96 | 97 | 3. Verify Go was uninstalled 98 | 99 | ```bash 100 | go version 101 | => -bash: go: command not found 102 | ``` 103 | 104 | 4. Visit [golang.org/dl](https://golang.org/dl) to install the latest Go. 105 | 106 | 107 | -------------------------------------------------------------------------------- /01_setup/exercise_1a.md: -------------------------------------------------------------------------------- 1 | # Exercise 1A: Find Stuff 2 | 3 | ## Goals 4 | 5 | - Familiarize yourself with the golang.org website and resources 6 | 7 | ## Setup 8 | 9 | - Visit `golang.org` 10 | 11 | ## Directions 12 | 13 | Answer the following questions 14 | 15 | 1. Read about `for loops` in the _Effective Go_ document 16 | 17 | - What kind of loop doesn’t exist in Go? 18 | 19 | 2. Read about the `fmt` _package_ 20 | 21 | - What does `fmt.Println()` return? 22 | 23 | 3. Find a _blog post_ about the recent release of Go 1.13 24 | 25 | - What are some of the new features? 26 | -------------------------------------------------------------------------------- /02_introduction/anatomy.md: -------------------------------------------------------------------------------- 1 | # Anatomy of a Go file 2 | 3 | Let's compare the basic file structures between Go and JavaScript. 4 | 5 | ```javascript 6 | import MyJSClass from '../MyJSClass' 7 | 8 | import React from 'react' 9 | 10 | // Access to MyJSClass and React methods and variables are now possible in this 11 | // file 12 | 13 | const Component = () => { 14 | // Code to execute 15 | console.log('Hello Front End Masters!') 16 | } 17 | 18 | export default Component 19 | ``` 20 | 21 | ```go 22 | package main // 1 23 | 24 | import "fmt" // 2 25 | 26 | // Access to main and fmt methods/variables is now possible in this file 27 | 28 | func main() { //3 29 | // Code to execute lives here 30 | fmt.Println("Hello Front End Masters!" 31 | } // 5 32 | ``` 33 | 34 | 1. Package Declaration 35 | 36 | - Packages are Go's way of organizing and reusing code within an application 37 | - These packages, like classes, expose different variables that can be used within a file 38 | - Every go program must have at least a `main` package 39 | - When you run `go install` Go creates a binary file which will call the `main()` function of your program 40 | - To find details on what a package does from your terminal, for instance `fmt`, you can run `go doc fmt` 41 | 42 | 2. Imported packages and libraries 43 | 44 | - Exposes code from built in go libraries, third party packages, or internally created packages your program needs 45 | - In this case, the package `fmt` comes with go and provides formatting methods for standard input and output 46 | - Also, it's pronounced "fumpt". 🙄 47 | 48 | 3. The `main()` go function signature 49 | 50 | - This function initializes the go program 51 | - Starts with a keyword `func` 52 | - The name of the function - `main` 53 | - A list of optional parameters - not present 54 | - An optional return statement - not present 55 | - Opening curly brace 56 | 57 | 4. Code to be executed 58 | 59 | 5. Closing curly brace 60 | 61 | - Indicates the end of a function 62 | - Is often preceded by a `return` statement, similar to JS 63 | -------------------------------------------------------------------------------- /02_introduction/code/exercise_2a_solution.go: -------------------------------------------------------------------------------- 1 | // package main 2 | 3 | // import "fmt" 4 | 5 | // // Note: only uncomment one main() function at a time when testing 6 | 7 | // // Part One 8 | // func main() { 9 | // fmt.Println("Hello World") 10 | // } 11 | 12 | // // Part Two 13 | // func main() { 14 | // fmt.Printf("Hi! My name is %s. I have lived in %s for %d years. They say the weather is amazing, which is %t", "Brenna", "Denver", 4, true) 15 | // } 16 | -------------------------------------------------------------------------------- /02_introduction/exercise_2a.md: -------------------------------------------------------------------------------- 1 | # Exercise 2a: Hello World 2 | 3 | ## Goals: 4 | 5 | - Use the `fmt` package 6 | - Practice string formatting 7 | 8 | ## Setup 9 | 10 | Create a file called `exercise_2a.go` in the `02_introduction/code` directory. 11 | 12 | If you're having environment issues, you can also use a sandbox like [Repl.it](https://repl.it/languages/go), or the [Go Playground](https://play.golang.org/). 13 | 14 | Start with the following code: 15 | 16 | ```go 17 | package main 18 | 19 | import "fmt" 20 | 21 | func main() { 22 | 23 | } 24 | ``` 25 | 26 | ## Directions 27 | 28 | Practice the following exercises: 29 | 30 | 1. Hello World 31 | - Print "Hello World" to the console. 32 | 33 | 2. More Printing 34 | - Using string formatting (`Printf`), print the following sentence to the console: 35 | - _Note_: Replace any `<>` with a value specific to you. 36 | 37 | `"Hi! My name is . I have lived in for years. They say the weather is amazing, which is ."` 38 | 39 | ==> `"Hi! My name is Brenna. I have lived in Denver for 4 years. They say the weather is amazing, which is true."` 40 | -------------------------------------------------------------------------------- /02_introduction/printing.md: -------------------------------------------------------------------------------- 1 | # Printing 2 | 3 | We've seen `fmt.Println` used to effectively log something the console. 4 | 5 | Note that this function can take multiple arguments, and will insert a space between 6 | arguments (which differs from having to manually insert them in JS 🙄). 7 | 8 | `fmt.Println()` 9 | 10 | ```go 11 | package main 12 | 13 | import "fmt" 14 | 15 | func main() { 16 | fmt.Println("All the best words start with B") 17 | fmt.Println("Bears, Beets, Battlestar Gallactica") 18 | } 19 | ``` 20 | 21 | `fmt.Print()`: Does not insert a new line 22 | 23 | ```go 24 | package main 25 | 26 | import "fmt" 27 | 28 | func main() { 29 | fmt.Print("All the best words start with B") 30 | fmt.Print("Bears, Beets, Battlestar Gallactica") 31 | } 32 | ``` 33 | 34 | `fmt.Printf()`: Uses string formatting to interpolate variables 35 | Note: 36 | `%t`: expects a boolean 37 | `%d`: expects a integer 38 | `%s`: expects a string 39 | 40 | ```go 41 | package main 42 | 43 | import "fmt" 44 | 45 | func main() { 46 | fmt.Printf("My name is %s, it is %t that I have %d brother.", "Marilyn", true, 1) 47 | } 48 | ``` 49 | 50 | ## Similar Functions and Patterns 51 | 52 | You'll notice that many of the functions that log or print will have similar 53 | flags with different underlying purpose. 54 | 55 | `fmt.Print()` 56 | 57 | - Prints output to the stdout console 58 | - Returns number of bytes and an error, although error is generally not 59 | worried about 60 | 61 | `fmt.Fprint()` 62 | 63 | - Prints the output to an external source (file, browser), not on the stdout console 64 | - Returns number of bytes, and any write errors 65 | 66 | `fmt.Sprint()`: 67 | 68 | - Stores output on a character buffer instead of spitting it out to the console 69 | - Returns the string you want to print 70 | 71 | ## Exercise 2a: Hello World 72 | 73 | `exercise_2a.md` 74 | -------------------------------------------------------------------------------- /03_basic_syntax/code/exercise_3a_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // func main() { 6 | // mySentence := "Hello this is a sentence." 7 | 8 | // for _, value := range mySentence { 9 | // fmt.Println(string(value)) 10 | // } 11 | // } 12 | -------------------------------------------------------------------------------- /03_basic_syntax/code/exercise_3b_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // func main() { 6 | // var name, hometown string 7 | // var time int 8 | // var weather bool 9 | 10 | // fmt.Println("Type your name") 11 | // fmt.Scan(&name) 12 | // fmt.Println("Type your hometown") 13 | // fmt.Scan(&hometown) 14 | // fmt.Println("Type the time lived in your hometown") 15 | // fmt.Scan(&time) 16 | // fmt.Println("Is the weather is nice (true/false)?") 17 | // fmt.Scan(&weather) 18 | // fmt.Printf("Hi! My name is %s. I have lived in %s for %d years. They say the weather is amazing, which is %t", name, hometown, time, weather) 19 | 20 | // } 21 | -------------------------------------------------------------------------------- /03_basic_syntax/code/for.go: -------------------------------------------------------------------------------- 1 | // // Uncomment the entire file 2 | 3 | package main 4 | 5 | // import "fmt" 6 | 7 | // func main() { 8 | 9 | // // // **************************** 10 | 11 | // // i := 1 12 | 13 | // for i := 1; i <= 100; i++ { 14 | // fmt.Println(i) 15 | // } 16 | 17 | // // // **************************** 18 | 19 | // // i := 1 20 | 21 | // // for i <= 100 { 22 | // // fmt.Println(i) 23 | // // // This will behave like a while loop 24 | // // i += 1 25 | // // } 26 | 27 | // // // **************************** 28 | 29 | // // var mySentence = "This is a sentence" 30 | 31 | // // for index, letter := range mySentence { 32 | // // fmt.Println("Index:", index, "Letter:", letter) 33 | // // } 34 | // } 35 | -------------------------------------------------------------------------------- /03_basic_syntax/code/ifs.go: -------------------------------------------------------------------------------- 1 | // // Uncomment this entire file 2 | 3 | package main 4 | 5 | // import ( 6 | // "fmt", 7 | // "errors" 8 | // ) 9 | 10 | // func someFunction() error { 11 | // return errors.New("some error") 12 | // } 13 | 14 | // func main() { 15 | 16 | // var someVar = 9 17 | 18 | // if someVar > 10 { 19 | // fmt.Println(someVar) 20 | // } 21 | 22 | // // // **************************** 23 | 24 | // // if someVar > 100 { 25 | // // fmt.Println("Greater than 100") 26 | // // } else if someVar == 100 { 27 | // // fmt.Println("Equals 100") 28 | // // } else { 29 | // // fmt.Println("Less than 100") 30 | // // } 31 | 32 | // // // **************************** 33 | // // err := someFunction() 34 | // // // => If this function returns a value, 35 | // // // => it will be an error of type Error 36 | 37 | // // // **************************** 38 | // // if err != nil { 39 | // // fmt.Println(err.Error()) 40 | // // } 41 | 42 | // // if err := someFunction(); err != nil { 43 | // // fmt.Println(err.Error()) 44 | // // } 45 | 46 | // // // End of file curly brace 47 | // } 48 | -------------------------------------------------------------------------------- /03_basic_syntax/code/scan.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // func main() { 6 | // var s1 string 7 | // var s2 string 8 | 9 | // fmt.Println("Enter two strings separated by a space") 10 | 11 | // // Ignore the & symbols for right now 12 | // numberOfArgs, err := fmt.Scan(&s1, &s2) 13 | 14 | // if err != nil { 15 | // fmt.Println(err) 16 | // } 17 | 18 | // fmt.Println("Number of args:", numberOfArgs, "String 1:", s1, "String 2:", s2) 19 | // } 20 | 21 | // // Bonus Exercise 3b: Revisiting Hello World 22 | // // 03_ctrl_structures/exercise_3b.md 23 | -------------------------------------------------------------------------------- /03_basic_syntax/code/switch.go: -------------------------------------------------------------------------------- 1 | // // Uncomment the entire file 2 | 3 | package main 4 | 5 | // import "fmt" 6 | 7 | // func main() { 8 | 9 | // var city string 10 | 11 | // switch city { 12 | // case "Des Moines": 13 | // fmt.Println("You live in Iowa") 14 | // case "Minneapolis,", "St Paul": 15 | // fmt.Println("You live in Minnesota") 16 | // case "Madison": 17 | // fmt.Println("You live in Wisconsin") 18 | // default: 19 | // fmt.Println("You're not from around here.") 20 | // } 21 | 22 | // // // **************************** 23 | // // var i int 24 | 25 | // // switch { 26 | // // case i > 10: 27 | // // fmt.Println("Greater than 10") 28 | // // case i < 10: 29 | // // fmt.Println("Less than 10") 30 | // // default: 31 | // // fmt.Println("Is 10") 32 | // // } 33 | 34 | // // // **************************** 35 | // // var i int = 9 36 | 37 | // // switch { 38 | // // case i != 10: 39 | // // fmt.Println("Does not equal 10") 40 | // // fallthrough 41 | // // case i < 10: 42 | // // fmt.Println("Less than 10") 43 | // // case i > 10: 44 | // // fmt.Println("Greater than 10") 45 | // // default: 46 | // // fmt.Println("Is 10") 47 | // // } 48 | // } 49 | -------------------------------------------------------------------------------- /03_basic_syntax/ctrl_structures.md: -------------------------------------------------------------------------------- 1 | # Control Structures 2 | 3 | ## If Statements 4 | 5 | If statements look similar to JavaScript, however in Go you exclude the 6 | parentheses around the conditional statement. 7 | 8 | An example if statement: 9 | 10 | ```go 11 | if someVar > 10 { 12 | // do some stuff 13 | } 14 | ``` 15 | 16 | Similarly, you have `else if` and `else` blocks. 17 | 18 | ```go 19 | if i > 100 { 20 | fmt.Println("Greater than 100") 21 | } else if i == 100 { 22 | fmt.Println("Equals 100") 23 | } else { 24 | fmt.Println("Less than 100") 25 | } 26 | ``` 27 | 28 | ### An Intro to Error Handling 29 | 30 | We will discuss error handling in depth later in this course, but lets dive in 31 | a bit now. 32 | 33 | First of all, a noticeable aspect of Go is the fact that as the developer you 34 | need to explicitly catch errors, and do something about them if they exist. One 35 | such approach might look like this: 36 | 37 | ```go 38 | err := someFunction() 39 | // => If this function returns a value, it will be an error of type Error 40 | 41 | if err != nil { 42 | fmt.Println(err.Error()) 43 | } 44 | ``` 45 | 46 | This is a very common situation, and Go let's us combine these lines into the 47 | same if block: 48 | 49 | ```go 50 | if err := someFunction(); err != nil { 51 | fmt.Println(err.Error()) 52 | } 53 | ``` 54 | ### If Blocks & Scope 55 | 56 | *PRO TIP* The `if` keyword kicks off it's own variable scope. 57 | 58 | This means that the variable `err` is scoped to this if block, which is helpful since we 59 | will be using this type of syntax all over the place. 60 | 61 | ## Switch 62 | 63 | An example switch statement: 64 | 65 | ```go 66 | var city string 67 | 68 | switch city { 69 | case "Des Moines": 70 | fmt.Println("You live in Iowa") 71 | case "Minneapolis,", "St Paul": 72 | fmt.Println("You live in Minnesota") 73 | case "Madison": 74 | fmt.Println("You live in Wisconsin") 75 | default: 76 | fmt.Println("You're not from around here.") 77 | } 78 | ``` 79 | 80 | Note that you can use multiple arguments in your case statement separated by a 81 | comma. 82 | 83 | A second version can contain no `switch expression` (represented by `city` after 84 | the switch keyword in the above statement) 85 | 86 | ```go 87 | var i int 88 | 89 | switch { 90 | case i > 10: fmt.Println("Greater than 10") 91 | case i < 10: fmt.Println("Less than 10") 92 | default: fmt.Println("Is 10") 93 | } 94 | ``` 95 | 96 | Note that in Go, if a case statement is executed, Go will not automatically 97 | continue running the switch statement. To ask for this behavior, you can add a 98 | `fallthrough` keyword. 99 | 100 | ```go 101 | var i int = 9 102 | 103 | switch { 104 | case i != 10: 105 | fmt.Println("Does not equal 10") 106 | fallthrough 107 | case i < 10: fmt.Println("Less than 10") 108 | case i > 10: fmt.Println("Greater than 10") 109 | default: fmt.Println("Is 10") 110 | } 111 | ``` 112 | 113 | ## For Loops 114 | 115 | The only loop in Go is the `for` loop. It looks nearly identical to our friendly for loop in JavaScript, but depending on how we write it it can behave in various ways. 116 | 117 | Like variables, we'll start with the most verbose version and work our way to idiomatic go. 118 | 119 | ```go 120 | func main() { 121 | i := 1 122 | for i <= 100 { 123 | fmt.Println(i) 124 | i += 1 125 | } 126 | } 127 | ``` 128 | 129 | Like in JS, we instantiate our counter `i` at 1, using shorthand variable assignment. Note that we do not need parentheses around the conditional. 130 | 131 | For every iteration, as long as our counter `i` is below 100, we print its value 132 | 133 | Increment the counter by one. 134 | 135 | We can also use a shorter way to write this that looks more like the version we all know and 136 | love in JS: 137 | 138 | ```go 139 | func main() { 140 | for i := 1; i <= 100; i++ { 141 | fmt.Println(i) 142 | } 143 | } 144 | ``` 145 | 146 | If we omit the first and third pieces of the opening statement (variable 147 | declaration and increment), the for loop will behave like a `while` loop: 148 | 149 | ```go 150 | func main() { 151 | for i < 100 { 152 | // do some stuff 153 | } 154 | } 155 | ``` 156 | 157 | ### Iterating Over A List 158 | 159 | For loops also let us iterate over a collection of elements, similar to how 160 | `forEach` might feel in JS (although we are not limited to an array). 161 | 162 | To do this we use the `range` keyword. 163 | 164 | ```go 165 | for index, element := range someCollection { 166 | // Do some stuff 167 | } 168 | ``` 169 | 170 | ## Exercise 3a: Control Structures 171 | `exercise_3a.md` 172 | -------------------------------------------------------------------------------- /03_basic_syntax/exercise_3a.md: -------------------------------------------------------------------------------- 1 | # Exercise 3a: Control Structures 2 | 3 | ## Goals: 4 | 5 | - Practice setting up a basic Go file 6 | - Declaring variables 7 | - Iterating over a collection using a for loop and the range keyword 8 | - Using if statements 9 | - Troubleshooting using covered topics 10 | 11 | ## Directions 12 | 13 | 1. Create a file called `exercise_3a.go` in the `03_ctrl_structures/code` directory. 14 | 15 | 2. In your `main` function, declare a variable that has a value of a sentence. 16 | 17 | 3. Iterate over that sentence using `range`. 18 | 19 | 4. If the index of that letter is an odd number, print that letter. 20 | 21 | 5. What do you notice? 22 | 23 | ## Hints 24 | 25 | _Hint_: You might need to convert a type to get the output you expect 26 | 27 | _Hint_: The modulo operator in Go functions similarly to 28 | JavaScript. 29 | -------------------------------------------------------------------------------- /03_basic_syntax/exercise_3b.md: -------------------------------------------------------------------------------- 1 | # Exercise 3b: (Bonus) Revisiting Hello World 2 | 3 | ## Goals 4 | 5 | - Practice using the `fmt.Scan()` function 6 | - Practice string formatting with `fmt.Printf()` 7 | 8 | ## Setup 9 | 10 | 1. Let's revisit part 2 of the Hello World exercise from section 2: 11 | 12 | ```go 13 | package main 14 | 15 | import "fmt" 16 | 17 | func main() { 18 | fmt.Printf("Hi! My name is %s. I have lived in %s for %d years. They say the weather is amazing, which is %t", "Brenna", "Denver", 4, true) 19 | } 20 | ``` 21 | 22 | ## Directions 23 | 24 | Instead of hard coding in these values, let's refactor our code to collect user 25 | input. 26 | 27 | 2. Using `fmt.Scan()`, ask the user for each of the missing variables, using a new `fmt.Println()` request for each variable: 28 | 29 | - Their name 30 | - What city they live in 31 | - How long they have lived there 32 | - If the weather is nice 33 | 34 | 3. Print the same sentence as part 2 of exercise_2a. 35 | 36 | ## Hints 37 | 38 | _Hint_: Don't forget to include the `&` symbol in the `Scan()` function. 39 | 40 | _Hint_: Arguments coming from the terminal will always be strings, might need 41 | to do some converting. 42 | 43 | _Hint_: Might need to pull in an additional package to handle string formatting, to make sure a user can type either "yes" or "YES" or "Yes" etc... 44 | 45 | -------------------------------------------------------------------------------- /03_basic_syntax/types.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | Go is a _statically typed_ language. Unlike JavaScript, variables must have a 4 | specified type and cannot change once they have been declared. If there is a 5 | discrepancy in types, your program won't compile and you'll get an error. 6 | 7 | Let's talk about Go's built in types. 8 | 9 | ## Integer: `1`, `2`, `44` 10 | 11 | - Example: `var age int = 21` 12 | - `int`,`uint8`,`uint16`, `uint32`, `uint64`, `int8`, `int16`, `int32`, `int64` 13 | - How specific you get depends on what type of processor you are working with (won't get into that 14 | now) 15 | - The `u` indicates an "unsigned" integer, which can only contain positive numbers or zero 16 | - `byte`: alias for `uint8` 17 | - `rune`: alias for `int32` 18 | 19 | ## Float: `1.5`, `3.14`, `2100` 20 | 21 | - Example: `var distanceInMiles float64 = 22.7` 22 | - Floats can contain a decimal point 23 | - `float32`, `float64` 24 | - To see a visual of the difference between how Go stores data in these two types, visit [this playground](https://play.golang.org/p/ZqzdCZLfvC) 25 | 26 | ## String: `"Marilyn Monroe"` 27 | 28 | - Example: `var name string = "Marilyn"` 29 | - Note that Go uses double quotes to indicate a string 30 | - Strings contain an immutable series of bytes that can be accessed by index 31 | - `fmt.Println(name[0])` 32 | - `fmt.Println(string(name[0]))` 33 | - Similar to `substr` in JS, you can grab a substring of a string using 34 | `name[1:3]` ==> `"ar"` 35 | - This tells Go to start BEFORE index 1, and stop BEFORE index 3. 36 | 37 | ## Booleans: `true`, `false` 38 | 39 | - Example: `var isLessThan bool = 1 < 5` 40 | - Can be generated using the expected logical and comparison operators 41 | - `&&`, `||`, `!`, `<, <=, >, >=, ==, !=` 42 | - Note: Go does not default "falsey" values to a boolean (like `0` or `""`) 43 | 44 | ## Error: `error` 45 | 46 | - Example: `error.Error()` ==> string 47 | - `log.Fatal(err)` will print the error message and stop execution 48 | 49 | ## TypeOf 50 | 51 | - To check the type of a variable, you can use the `TypeOf()` method from the package `reflect`. 52 | 53 | ```go 54 | import "fmt" 55 | import "reflect" 56 | 57 | func main() { 58 | var x = "What am I" 59 | fmt.Println(reflect.TypeOf(x)) 60 | } 61 | ``` 62 | 63 | - You can also use string formatting within `fmt` to print out a type: 64 | - Note that `%v` is a generic verb for "value", `%T` is a verb for Type. 65 | 66 | ```go 67 | var x = "What am I" 68 | fmt.Printf("The type of `%v` is %T", x, x) 69 | ``` 70 | 71 | ## Type Conversion 72 | 73 | Sometimes you'll want to convert one type to another. Go lets you do this using the type name as a function. 74 | 75 | ```go 76 | var age int = 21 77 | var floatAge = float64(age) 78 | fmt.Println(reflect.TypeOf(age)) 79 | fmt.Println(reflect.TypeOf(floatAge)) 80 | ``` 81 | 82 | Note: Certain types, like strings, need an additional library (`strconv`) to be converted. 83 | 84 | -------------------------------------------------------------------------------- /03_basic_syntax/variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | Variables in Go are written in camel case, similar to JavaScript, and can be defined a few different ways. 4 | 5 | **Option 1:** 6 | 7 | - Initialize a variable with a type and the keyword `var` 8 | - Looks similar to JavaScript with an additional type declaration 9 | 10 | ```go 11 | var name string = "Beyonce" 12 | ``` 13 | 14 | **Option 2:** 15 | 16 | - Go can infer the type of initialized variables and the type declaration can be 17 | omitted. 18 | 19 | ```go 20 | var name = "Beyonce" 21 | ``` 22 | 23 | **Option 3:** 24 | 25 | - Feels similar to using `let` in JavaScript to instantiate and empty variable, but instead of storing the value as `undefined`, Go will default to the zero value for that type 26 | - string: `""` 27 | - int: `0` 28 | - float: `0` 29 | - bool: `false` 30 | 31 | ```go 32 | var name string 33 | // This will have a default value of "" that can be modified later 34 | ``` 35 | 36 | **Option 4:** 37 | 38 | - Declare multiple variables at once 39 | 40 | ```go 41 | var name1, name2 string = "Beyonce", "Lizzo" 42 | ``` 43 | 44 | **Option 5:** 45 | 46 | - Can only be used within a function 47 | - Most commonly used pattern 48 | - To declare and assign a default value at the same time 49 | - You can omit the keyword `var`, and go will infer the value 50 | 51 | ```go 52 | name := "Beyonce" 53 | ``` 54 | 55 | #### Var vs Const 56 | 57 | Consts, like in JavaSript, are variables whose values cannot be changed. 58 | Attempting to modify a const will result in a run-time error. 59 | 60 | **Sidebar** 61 | 62 | - RunTime vs CompileTime 63 | - Reminder that compile time errors involve syntax or type-checking. They occur when you, as the developer, are compiling your code. 64 | - Run time errors happen after the program is compiled and a user or browser is trying to execute that code. 65 | - Examples of a run time error: Trying to open a file or url that doesn't exist, running out of memory, trying to do something that syntactically is legit but isn't valid in real life (like dividing by 0). 66 | -------------------------------------------------------------------------------- /04_complex_structures/arrays.md: -------------------------------------------------------------------------------- 1 | # Arrays 2 | 3 | Arrays in Go have some significant differences compared to those in JavaScript. 4 | 5 | Let's compare notes. 6 | 7 | **JavaScript:** 8 | 9 | ```javascript 10 | // Initialize an empty array 11 | const grabBag = [] 12 | // Eventually this array could have values that represent these types: 13 | const grabBag = [string, int, boolean, float64] 14 | // or 15 | const grabBag = [string, string, string, string, integer, boolean, float64] 16 | ``` 17 | 18 | **Go:** 19 | 20 | ```go 21 | // Initialize an empty array 22 | var scores [5]float64 23 | // Eventually this array can ONLY contain floats and a length of 5: 24 | [float64, float64, float64, float64, float64] 25 | ``` 26 | 27 | ## Defining An Array 28 | 29 | In JS, defining an array is pretty chill. 30 | 31 | You can make it whatever length you want, you can modify that length, and you can throw any combination of data 32 | types into it. 33 | 34 | Go, however, has _opinions_. An array type definition must include a specified length of elements, all of which must be of the same type. In the above example, our variable `scores` is an array of 5 elements, each of which is a float. 35 | 36 | Note: An array's length is fixed with the exact length included in its type 37 | definition. This means that `[5]float64` and `[6]float64` are different, 38 | distinct, unequal types. 39 | 40 | > _TRY IT_ 41 | > In the Go playground, print out the variable `scores` as is. What do you see? 42 | 43 | To add elements into this array, both JavaScript and Go allow you to insert elements into a specific index location. 44 | 45 | ```javascript 46 | grabBag[0] = 'hello' // => ["hello"] 47 | ``` 48 | 49 | ```go 50 | ages[0] = 5 // => [5, 0, 0, 0, 0] 51 | ``` 52 | 53 | To add multiple elements, you could start by doing something like this: 54 | 55 | ```go 56 | var scores [5]float64 57 | scores[0] = 9 58 | scores[1] = 1.5 59 | scores[2] = 4.5 60 | scores[3] = 7 61 | scores[4] = 8 62 | ``` 63 | 64 | Or, to avoid setting each index individually, we can use a _composite literal)_ 65 | which containes the type declaration, and a set of initial values: 66 | 67 | ```go 68 | scores := [5]float64{9, 1.5, 4.5, 7, 8} 69 | ``` 70 | 71 | Going one step further, Go will infer the length of an array when you provide an 72 | initial set of values, and the length can be replaced with an ellipsis. 73 | 74 | ```go 75 | scores := [...]float64{9, 1.5, 4.5, 7, 8} 76 | ``` 77 | 78 | > _TRY IT_ 79 | > Using the array above and a for loop, print out the average score within the array. 80 | > Hint: To find the length of an array if you don't know it, use `len(array)`. 81 | 82 | What unexpected errors did you run into? How did you fix them? 83 | 84 | ## Revisiting Range 85 | 86 | When looping over arrays, we can implement a different version of the basic for loop using the `range` keyword: 87 | 88 | ```go 89 | var total float64 90 | for _, value := range scores { 91 | total += value 92 | } 93 | ``` 94 | 95 | Let's break it down: 96 | 97 | - `i` Still represents the index position we are pointing to in our array 98 | - `value` represents the value of the element at that index (aka `scores[i]`) 99 | - `range` is a keyword that is followed by the variable representing the array we are looping over 100 | 101 | > _TRY IT_ 102 | > Update the previous for loop from finding the average scores to use `range`. 103 | 104 | As we've seen, Go is very particular about types and making sure any declared variable is used within a program. In this case, we aren't using the `i` variable representing the index of the array and we get this error: 105 | 106 | ```bash 107 | i declared and not used 108 | ``` 109 | 110 | Go uses a single underscore, `_`, to replace an unused variable within a program. Make this modification and watch the program pass. 111 | 112 | Defining an array with a specific length can cause some obvious problems. 113 | 114 | Adding or removing an element would require redefining the entire variable signature to specify a different length, which often is unknown. 115 | 116 | This brings us to Slices. 117 | -------------------------------------------------------------------------------- /04_complex_structures/code/exercise_4a_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // ) 6 | 7 | // // func average(num1, num2, num3 float64) float64 { 8 | // // total := num1 + num2 + num3 9 | // // return total / 3 10 | // // } 11 | 12 | // func average(numbers ...float64) float64 { 13 | // total := 0.0 14 | // for _, number := range numbers { 15 | // total += number 16 | // } 17 | // return total / float64(len(numbers)) 18 | // } 19 | 20 | // func main() { 21 | // fmt.Println(average(10, 5, 7)) 22 | // } 23 | -------------------------------------------------------------------------------- /04_complex_structures/code/exercise_4b_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // ) 6 | 7 | // func average(num1, num2, num3 float64) float64 { 8 | // total := num1 + num2 + num3 9 | // return total / 3 10 | // } 11 | 12 | // func main() { 13 | // fmt.Println(average(10, 5, 7)) 14 | // } 15 | -------------------------------------------------------------------------------- /04_complex_structures/code/exercise_4c_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // ) 6 | 7 | // func average(numbers ...float64) float64 { 8 | // total := 0.0 9 | // for _, number := range numbers { 10 | // total += number 11 | // } 12 | // return total / float64(len(numbers)) 13 | // } 14 | 15 | // func main() { 16 | // fmt.Println(average(10, 5, 7)) 17 | // } 18 | -------------------------------------------------------------------------------- /04_complex_structures/code/functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // func printAge(age1, age2 int) (ageOfSally, ageOfBob int) { 6 | // ageOfSally = age1 7 | // ageOfBob = age2 8 | // return 9 | // } 10 | 11 | func main() { 12 | // x, y := printAge(10, 21) 13 | // fmt.Println(x) 14 | // fmt.Println(y) 15 | } 16 | -------------------------------------------------------------------------------- /04_complex_structures/code/maps.go: -------------------------------------------------------------------------------- 1 | // // Uncomment the entire file 2 | 3 | package main 4 | 5 | // import "fmt" 6 | 7 | // func main() { 8 | 9 | // var userEmails map[int]string 10 | 11 | // userEmails[1] = "user1@gmail.com" 12 | // userEmails[2] = "user2@gmail.com" 13 | 14 | // fmt.Println(userEmails) 15 | 16 | // // **************************** 17 | 18 | // var userEmails map[int]string = make(map[int]string) 19 | // // userEmails := make(map[int]string) 20 | 21 | // userEmails[1] = "user1@gmail.com" 22 | // userEmails[2] = "user2@gmail.com" 23 | 24 | // fmt.Println(userEmails) 25 | 26 | // // **************************** 27 | 28 | // userEmails := map[int]string{ 29 | // 1: "user1@gmail.com", 30 | // 2: "user2@gmail.com", 31 | // } 32 | 33 | // fmt.Println(userEmails) 34 | 35 | // // **************************** 36 | 37 | // userEmails := map[int]string{ 38 | // 1: "user1@gmail.com", 39 | // 2: "user2@gmail.com", 40 | // } 41 | 42 | // fmt.Println(userEmails) 43 | 44 | // fmt.Println(userEmails[1]) 45 | 46 | // // **************************** 47 | 48 | // userEmails := map[int]string{ 49 | // 1: "user1@gmail.com", 50 | // 2: "user2@gmail.com", 51 | // } 52 | 53 | // fmt.Println(userEmails) 54 | 55 | // userEmails[1] = "newUser1@gmail.com" 56 | 57 | // fmt.Println(userEmails) 58 | 59 | // fmt.Println(userEmails[3]) 60 | 61 | // // **************************** 62 | 63 | // userEmails := map[int]string{ 64 | // 1: "user1@gmail.com", 65 | // 2: "user2@gmail.com", 66 | // } 67 | 68 | // email1, ok := userEmails[1] 69 | // fmt.Println("Email:", email1, "Present?", ok) 70 | 71 | // // email3, ok := userEmails[3] 72 | // // fmt.Println("Email", email3, "Present?", ok) 73 | 74 | // // **************************** 75 | 76 | // userEmails := map[int]string{ 77 | // 1: "user1@gmail.com", 78 | // 2: "user2@gmail.com", 79 | // } 80 | 81 | // if email, ok := userEmails[1]; ok { 82 | // fmt.Println(email) 83 | // } else { 84 | // fmt.Println("I don't know what you want from me") 85 | // } 86 | 87 | // // **************************** 88 | 89 | // userEmails := map[int]string{ 90 | // 1: "user1@gmail.com", 91 | // 2: "user2@gmail.com", 92 | // } 93 | 94 | // delete(userEmails, 2) 95 | 96 | // fmt.Println(userEmails) 97 | // // **************************** 98 | 99 | // userEmails := map[int]string{ 100 | // 1: "user1@gmail.com", 101 | // 2: "user2@gmail.com", 102 | // } 103 | 104 | // for k, v := range userEmails { 105 | // fmt.Printf("%s has an ID of %d.\n", v, k) 106 | // } 107 | // // **************************** 108 | // } 109 | -------------------------------------------------------------------------------- /04_complex_structures/code/slices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // func main() { 6 | 7 | // var myArray [5]int 8 | // var mySlice []int 9 | 10 | // myArray[0] = 1 11 | // mySlice[0] = 1 12 | 13 | // fmt.Println(myArray) 14 | // fmt.Println(mySlice) 15 | 16 | // // *************************** 17 | 18 | // // var myArray [5]int 19 | // // var mySlice []int = make([]int, 5) 20 | 21 | // // fmt.Println(myArray) 22 | // // fmt.Println(mySlice) 23 | 24 | // // *************************** 25 | 26 | // // var myArray [5]int 27 | // // // var mySlice []int = make([]int, 5) 28 | // // var mySlice []int = make([]int, 5, 10) 29 | // // // var mySlice = make([]int, 5, 10) 30 | 31 | // // myArray[0] = 1 32 | // // mySlice[0] = 1 33 | 34 | // // fmt.Println(myArray) 35 | // // fmt.Println(mySlice) 36 | // // fmt.Println(len(mySlice)) 37 | // // fmt.Println(cap(mySlice)) 38 | 39 | // // *************************** 40 | 41 | // // fruitArray := [5]string{"banana", "pear", "apple", "kumquat", "peach"} 42 | 43 | // // var splicedFruit []string = fruitArray[1:3] // ==> ["pear", "apple",] 44 | 45 | // // fmt.Println(len(splicedFruit)) 46 | // // fmt.Println(cap(splicedFruit)) 47 | 48 | // // *************************** 49 | 50 | // // SEE SLIDE 51 | 52 | // // *************************** 53 | 54 | // // slice1 := []int{1, 2, 3} 55 | // // slice2 := append(slice1, 4, 5) 56 | 57 | // // fmt.Println(slice1, slice2) 58 | // // fmt.Println(len(slice1), cap(slice1)) 59 | // // fmt.Println(len(slice2), cap(slice2)) 60 | 61 | // // *************************** 62 | 63 | // // originalSlice := []int{1, 2, 3} 64 | // // destination := make([]int, len(originalSlice)) 65 | 66 | // // fmt.Println("Before Copy:", originalSlice, destination) 67 | 68 | // // mysteryValue := copy(destination, originalSlice) 69 | 70 | // // // fmt.Println("After Copy:", originalSlice, destination, mysteryValue) 71 | // } 72 | -------------------------------------------------------------------------------- /04_complex_structures/exercise_4a.md: -------------------------------------------------------------------------------- 1 | # Exercise 4a: Functions 2 | 3 | ## Goals 4 | 5 | - Practice writing functions 6 | 7 | ## Setup 8 | 9 | 1. Create a file called `exercise_4a.go` in the `04_complex_structures/code` directory. 10 | 11 | 2. Create a function called `average` that takes three arguments separate arguments, all of which are floats. 12 | 13 | 3. The function should return the average of the three arguments as a float. 14 | 15 | 4. Make sure to call this function from within `main()` -------------------------------------------------------------------------------- /04_complex_structures/exercise_4b.md: -------------------------------------------------------------------------------- 1 | # Exercise 4b: Refactoring Functions 2 | 3 | ## Goals 4 | 5 | - Practice variadic functions 6 | - Practice and review managing types 7 | 8 | ## Setup 9 | 10 | - Feel free to refactor the code from `exercise_4a.go`, or create a new file called `exercise_4b.go` in the same directory. 11 | 12 | ## Directions 13 | 14 | 1. Refactor your code to use a variadic function that takes in an unknown number of arguments. 15 | 16 | ## Hints 17 | 18 | _HINT_: To find the length of a collection, use `len(someCollection)`. 19 | 20 | _HINT_: Check the type of `len(someCollection)` -------------------------------------------------------------------------------- /04_complex_structures/exercise_4c.md: -------------------------------------------------------------------------------- 1 | # Exercise 4c: Functions, Arrays, Slices, Maps, 2 | 3 | ## Goals 4 | 5 | - Practice working with arrays, slices, and maps 6 | - Review `range` 7 | 8 | ## Setup 9 | 10 | - Create a file called `exercise_4c.go` in the `04_complex_structures/code` directory. 11 | 12 | ## Directions 13 | 14 | ### Part 1 15 | 16 | 1. Instantiate an array of scores 17 | - The array should have at least 5 elements of type `float64` 18 | 19 | 2. Write a function that calculates and returns the average score (also a float) 20 | - Use the `range` keyword 21 | 22 | 23 | ### Part 2 24 | 25 | 1. Define a map that contains a set of pet names, and their corresponding animal type. i.e.: `"fido": "dog"`. 26 | 27 | 2. Write a function that takes a string argument and returns a boolean indicating whether or not that key exists in your map of pets. 28 | 29 | 30 | ### Part 3 31 | 32 | 1. Instantiate a slice that has an initial value of a collection of groceries. 33 | 34 | 2. Write a function that takes one or more groceries as strings and appends them to the slice, printing out the resulting list of groceries. -------------------------------------------------------------------------------- /04_complex_structures/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Functions in Go, like in most languages, map a specified set of inputs to specified outputs. 4 | 5 | In Go, the input parameters also require a type definition, with an optional return type definition. 6 | 7 | ```go 8 | func printAge(age int) int { 9 | fmt.Println(age) 10 | return age 11 | } 12 | ``` 13 | In the above function, we name the function `printAge()`, which expects an input 14 | type of an integer. The value between the closing argument parens and the opening 15 | curly brace is the type this function must return at the end of the day. 16 | 17 | ## Multiple Returns 18 | 19 | A function can also return multiple values, which both need to be specified in 20 | the return type definition: 21 | 22 | ```go 23 | func myFunc() (int, int) { 24 | return 5, 6 25 | } 26 | 27 | func main() { 28 | x, y := myFunc() 29 | } 30 | ``` 31 | 32 | ## Named Returns 33 | 34 | ```go 35 | func myFunc() (ageOfBob int, ageOfSally int) { 36 | ageOfBob = 21 37 | ageOfSally = 25 38 | // return ageOfBob, ageOfSally 39 | return 40 | } 41 | 42 | func main() { 43 | x, y := myFunc() 44 | } 45 | ``` 46 | 47 | Note that when calling this function, in order to access both return values you 48 | also need to define two variables. 49 | 50 | ## Exercise 4a: Functions 51 | `exercise_4a_functions.md` 52 | 53 | ## Variadic Function 54 | 55 | Similar to the spread/rest operator in JavaScript, you can pass an unspecified number 56 | of arguments to a function, all of which are stored as a `slice` of the same type of element, which we 57 | will talk about shortly. 58 | 59 | ```go 60 | func doThings(args ...int) int { 61 | total := 0 62 | for _, num := range args { 63 | total += num 64 | } 65 | return total 66 | } 67 | 68 | func main() { 69 | fmt.Println(doThings(1, 2, 3)) 70 | } 71 | ``` 72 | 73 | Note that the variadic parameter must be the last on in the argument list. 74 | 75 | ## Exercise 4b: Refactor Functions 76 | `exercise_4b_functions.md` 77 | 78 | ### Scope 79 | 80 | Similar to JS, variables defined within a function can only be accessed within that function. 81 | 82 | To use a variable in other functions within the program, you must define it externally. 83 | 84 | ```go 85 | func otherFunc() { 86 | fmt.Println(user) // pilot will be undefined 87 | } 88 | 89 | func main() { 90 | var user string = "Marilyn Monroe" 91 | fmt.Println(user) 92 | } 93 | ``` 94 | 95 | Note: Functions do not have access to variables that are not global, or defined within 96 | themselves. 97 | 98 | ### Block Scope 99 | 100 | Note that curly braces also indicate their own scope. Variables defined within a 101 | set of curly braces are not accessible outside of those curly braces. 102 | 103 | This is similar to if blocks (which we saw earlier). 104 | 105 | ## More On Functions 106 | 107 | ### Functions As Types 108 | 109 | Functions can also be stored as variables, with their function signature as 110 | their type. 111 | 112 | ```go 113 | func isOlderThanTen(age int) bool { 114 | return age > 10 115 | } 116 | 117 | func isNotZero(num int) bool { 118 | return num != 0 119 | } 120 | 121 | var myFuncVariable func(int) bool 122 | 123 | func main() { 124 | myFuncVariable = isOlderThanTen 125 | fmt.Println(myFuncVariable(9)) // ==> false 126 | 127 | myFuncVariable = isNotZero 128 | fmt.Println(myFuncVariable(9)) // ==> true 129 | } 130 | ``` 131 | 132 | ### IIFE 133 | 134 | You can immediately call an unnamed function, similar to working with an IIFE in 135 | JS. 136 | 137 | ```go 138 | var age = 9 139 | 140 | var isOlderThanTen bool = func() bool { 141 | return age > 10 142 | }() 143 | 144 | fmt.Println(isOlderThanTen) 145 | ``` -------------------------------------------------------------------------------- /04_complex_structures/maps.md: -------------------------------------------------------------------------------- 1 | # Maps 2 | 3 | Maps look and behave similarly to Objects in JavaScript, using a set of key 4 | value pairs. 5 | 6 | A map starts with the keyword `map`, followed by the key type, and the 7 | value type. 8 | 9 | ```go 10 | var userEmails map[int]string 11 | ``` 12 | 13 | Here we are telling Go to create a map called `userEmails` which will have keys as 14 | integers (think IDs), and values as strings that will represent each email 15 | address. 16 | 17 | Let's build one with data in it: 18 | 19 | ```go 20 | var userEmails map[int]string 21 | userEmails[1] = "user1@gmail.com" 22 | userEmails[2] = "user2@gmail.com" 23 | fmt.Println(userEmails) 24 | ``` 25 | 26 | > _TRY IT_ 27 | > Throw that code into the Go playground. What happens? What are we missing? 28 | 29 | If we run the above code as-is, we'll see the following error: 30 | 31 | ```go 32 | panic: assignment to entry in nil map 33 | ``` 34 | 35 | The (run-time!) error here, indicated by the keyword `panic`, is telling us that 36 | we're trying to assign entries to a map that doesn't exist yet. 37 | 38 | Look at where we are declaring our `userEmails` variable. Here, we are telling 39 | Go to create a variable called `userEmails` that will have a type of map with 40 | particular key value pair types. We haven't actually set that variable to a 41 | value of any kind, nor have we allocated any memory where it can be stored. 42 | 43 | Remember the `make` function from our slice examples? Let's do that now. 44 | 45 | ```go 46 | var userEmails map[int]string 47 | userEmails = make(map[int]string) 48 | 49 | userEmails[1] = "user1@gmail.com" 50 | userEmails[2] = "user2@gmail.com" 51 | fmt.Println(userEmails) 52 | ``` 53 | 54 | Or, with the shorthand syntax: 55 | 56 | ```go 57 | userEmails := make(map[int]string) 58 | userEmails[1] = "user1@gmail.com" 59 | userEmails[2] = "user2@gmail.com" 60 | fmt.Println(userEmails) 61 | ``` 62 | 63 | Now we should see: 64 | `map[1:user1@gmail.com 2:user2@gmail.com]` 65 | 66 | As with arrays, we can simplify creating our map using abbreviated syntax. Note 67 | that here we don't need the `make` keyword since we are providing Go with an 68 | initial amount of data to store in memory: 69 | 70 | ```go 71 | userEmails := map[int]string{ 72 | 1: "user1@gmail.com", 73 | 2: "user2@gmail.com", 74 | } 75 | fmt.Println(userEmails) 76 | ``` 77 | 78 | ## Mutating and Reading Maps 79 | 80 | To ask for a specific value from a map, you'd use syntax similar to interacting 81 | with objects in JS: 82 | 83 | ```go 84 | userEmails[1] // ==> user1@gmail.com 85 | ``` 86 | 87 | To modify an element in an array, simply reassign its value: 88 | 89 | ```go 90 | userEmails[1] = "newUser1@gmail.com" 91 | fmt.Println(userEmails) 92 | ``` 93 | 94 | > _TRY IT_ 95 | > What happens if you ask for a key that doesn't exist? Try asking for 96 | > `userEmails[3]`. 97 | 98 | Go has a clever way to handle checking for null values. Looking up a value 99 | within a map actually returns two elements: the value (if it exists), and a 100 | boolean for whether or not that value existed. 101 | 102 | ```go 103 | firstEmail, ok := userEmails[1] 104 | missingEmail, ok := userEmails[3] 105 | fmt.Println("Email:", firstEmail, "Present?", ok) 106 | fmt.Println("Email", missingEmail, "Present?", ok) 107 | ``` 108 | 109 | This makes it easy to add logic around checking for null values within our 110 | program using an if statement: 111 | 112 | ```go 113 | if email, ok := userEmails[1]; ok { 114 | fmt.Println(email) 115 | } else { 116 | fmt.Println("I don't know what you want from me") 117 | } 118 | ``` 119 | 120 | First, we create the two variables `email` and `ok` inside our if 121 | statement and set them to the two elements we get back from a lookup in our map. 122 | 123 | Then, we check what boolean we got back in our `ok` variable. The code within 124 | the first block will fire only if `ok` returns true, otherwise our if block will 125 | continue to the else block. 126 | 127 | ### Deleting 128 | 129 | ```go 130 | delete(userEmails, 2) 131 | ``` 132 | 133 | ### Iterating 134 | 135 | We can iterate over a map by using the familiar `range` keyword, however now our 136 | for loop will be iterating over the `key` and `value` variables. 137 | 138 | ```go 139 | for k, v := range userEmails { 140 | fmt.Printf("%s has an ID of %d.\n", v, k) 141 | } 142 | ``` 143 | 144 | -------------------------------------------------------------------------------- /04_complex_structures/slices.md: -------------------------------------------------------------------------------- 1 | ## Make 2 | 3 | Before we get into slices, let's talk about a specific Go builtin function called 4 | `make()`. 5 | 6 | According to [the docs](https://golang.org/pkg/builtin/#make), the make function allocates space in memory for, and initializes a `slice`, `map`, or `channel`. 7 | 8 | This is necessary because, as seen with Arrays, Go is anticipating the need to set 9 | aside a specific length of memory for a variable being created. Since `slice` 10 | and `map` allow you to define a variable _without_ a set length, `make` helps Go 11 | establish where this variable will live and how to access it. 12 | 13 | In the example below, we are initializing an array, and a slice. 14 | 15 | ```go 16 | var myArray [5]int 17 | var mySlice []int 18 | 19 | myArray[0] = 1 20 | mySlice[0] = 2 21 | 22 | fmt.Println(myArray) 23 | fmt.Println(mySlice) 24 | ``` 25 | 26 | > TRY_IT 27 | > What happens when you print the variables above? 28 | 29 | If that was all of the code we had, Go would have no way of knowing how much memory to allocate for the slice, since it has no association with an underlying array (yet). This is where `make` comes in. 30 | 31 | Because slices are _segments of an array_, they must always be associated with an underlying array. 32 | 33 | ```go 34 | var myArray [5]int 35 | // var mySlice []int = make([]int, 5) 36 | var mySlice []int = make([]int, 5, 10) 37 | // var mySlice = make([]int, 5) 38 | 39 | myArray[0] = 1 40 | mySlice[0] = 2 41 | 42 | fmt.Println(myArray) 43 | fmt.Println(mySlice) 44 | fmt.Println(len(mySlice)) 45 | fmt.Println(cap(mySlice)) 46 | ``` 47 | 48 | ## Slice 49 | 50 | A slice is a segment of an array. Like an array, a slice must contain a single type of element. Elements can still be accessed by their index, a slice must start with an initial length, but unlike arrays their lengths can change. 51 | 52 | Let's say we have a fixed array that looks like this: 53 | 54 | ```go 55 | fruitArray := [5]string{"banana", "pear", "apple", "kumquat", "peach"} 56 | ``` 57 | 58 | We could access and create a slice of that by specifying two indices from within 59 | that array, separated by a colon. 60 | 61 | In the following example, we are asking for elements starting at index 1, up to but 62 | not including index 3. 63 | 64 | ```go 65 | var splicedFruit []string = fruit[1:3] // ==> ["pear", "apple",] 66 | ``` 67 | 68 | // SEE SLIDE 69 | 70 | NOTE: When passing a slice around to various functions, you are essentially 71 | passing the _header_ of the slice, the "summary" of what that slice references, 72 | which includes a hidden pointer to the underlying array. 73 | 74 | 75 | `fruit[:3]` ==> Everything up to (but not including) index 3 76 | `fruit[3:]` ==> Everything after (and including) index 3 77 | 78 | Instantiating a slice (without setting aside any memory for it) looks almost identical to instantiating an array, just without a specific length. 79 | 80 | ```go 81 | var scores[]float64 82 | ``` 83 | 84 | 85 | ### Len, Cap 86 | 87 | The associated underlying array can be visualized by using built in functions to see the difference. 88 | 89 | ``` 90 | fruitArray := [5]string{"banana", "pear", "apple", "kumquat", "peach"} 91 | var splicedFruit []string = fruitArray[1:3] // ==> ["pear", "apple",] 92 | ``` 93 | 94 | _Try It_ 95 | What happens when you ask for the `len()` of the array? (Length) 96 | What about the `cap()` (Capacity) 97 | 98 | This indicates that the longest this slice can be, is the length of its 99 | underlying array. 100 | 101 | 102 | What if you want to increase the length of a slice? 103 | 104 | 105 | 106 | ### Slice Helper Methods 107 | 108 | There are built in helper methods associated with the `slice` type, similar to 109 | prototype methods in JavaScript. The one we will use later in this course is 110 | `append`. 111 | 112 | #### Append: 113 | 114 | - `append(originalSlice, newEl1, newEl2)` 115 | 116 | ```go 117 | slice1 := []int{1, 2, 3} 118 | slice2 := append(slice1, 4, 5) 119 | fmt.Println(slice1, slice2) 120 | ``` 121 | 122 | Another interesting method that takes a second to think through coming from the 123 | FE is go's `copy`. 124 | 125 | #### Copy: 126 | 127 | What do you expect the following `mysteryValue` variable to print out? 128 | 129 | ```go 130 | originalSlice := []int{1, 2, 3} 131 | destination := make([]int, len(originalSlice)) 132 | fmt.Println("Before Copy:", originalSlice, destination) 133 | 134 | mysteryValue := copy(destination, originalSlice) 135 | 136 | fmt.Println("After Copy:", originalSlice, destination, mysteryValue) 137 | ``` 138 | 139 | > _TRY IT_ 140 | > Run the example code from the Copy block. 141 | > What do you notice about the value of `mysteryValue`? 142 | 143 | Check out [this blog 144 | post](https://blog.golang.org/go-slices-usage-and-internals) for more great info 145 | on slices and arrays. -------------------------------------------------------------------------------- /05_toolkit/code/exercise_5a_solution/packages.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fem-intro-to-go/05_toolkit/code/utils" 5 | "fmt" 6 | ) 7 | 8 | func calculateImportantData() int { 9 | totalValue := utils.Add(1, 2, 3, 4, 5) 10 | return totalValue 11 | } 12 | 13 | func main() { 14 | fmt.Println("Packages!") 15 | total := calculateImportantData() 16 | fmt.Println(total) 17 | } 18 | -------------------------------------------------------------------------------- /05_toolkit/code/exercise_5a_solution/utils/add_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAdd(t *testing.T) { 8 | expected := 4 9 | actual := Add(2, 2) 10 | 11 | if actual != expected { 12 | t.Errorf("Add function does not add up: Expected: %d, Actual: %d", expected, actual) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /05_toolkit/code/exercise_5a_solution/utils/math.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "fmt" 4 | 5 | func printNum(num int) { 6 | fmt.Println("Current Number:", num) 7 | } 8 | 9 | // Add adds together multiple numbers 10 | func Add(nums ...int) int { 11 | total := 0 12 | for _, v := range nums { 13 | printNum(v) 14 | total += v 15 | } 16 | return total 17 | } 18 | -------------------------------------------------------------------------------- /05_toolkit/code/exercise_5a_solution/utils/strings.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "strings" 4 | 5 | // MakeExcited transforms a sentence to all caps with an exclamation point 6 | func MakeExcited(sentence string) string { 7 | return strings.ToUpper(sentence) + "!" 8 | } 9 | -------------------------------------------------------------------------------- /05_toolkit/code/exercise_5a_solution/utils/strings_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func TestMakeExcited(t *testing.T) { 6 | expected := "OMG SO EXCITING!" 7 | actual := MakeExcited("omg so exciting") 8 | if actual != expected { 9 | t.Errorf("Average was incorrect! Expected: %s, Actual: %s", expected, actual) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /05_toolkit/code/packages.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | fmt.Println("Packages!") 9 | } 10 | -------------------------------------------------------------------------------- /05_toolkit/code/tools.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | go run main.go 4 | 5 | go install 6 | 7 | go build 8 | 9 | go fmt file.go 10 | 11 | go list 12 | 13 | go vet 14 | 15 | go doc fmt.Println 16 | 17 | go get golang.org/x/lint/golint 18 | 19 | */ 20 | -------------------------------------------------------------------------------- /05_toolkit/exercise_5a.md: -------------------------------------------------------------------------------- 1 | # Exercise 5a: Testing the Add Method 2 | 3 | ## Goals 4 | 5 | - Write a unit test 6 | - Use the TDD method to implement code to make a test pass 7 | 8 | ## Setup 9 | 10 | - Reference the `utils/math.go` file that contains our previously written `Add` function. 11 | - Create a `strings_test.go` file in the utils directory, and copy the test provided at the bottom of this file (this will be used for Part 2) 12 | 13 | ## Directions 14 | 15 | ### Part 1: Write A Test 16 | 17 | 1. In the previous section we build a custom `utils` package, that had an `Add` method. 18 | 19 | 2. Add an appropriately named test file, and write a test for this function. 20 | 21 | 3. Run the test with `go test` and make sure it's passing. 22 | 23 | ### Part 2: Use TDD to Write A Function 24 | 25 | 1. In the utils directory navigate to your newly created `strings_test.go` file. 26 | 27 | 2. Copy the following code into that test file, and watch the test fail. 28 | 29 | 3. Implement the necessary code to make this test pass. 30 | 31 | ```go 32 | // strings_test.go 33 | package utils 34 | 35 | import "testing" 36 | 37 | func TestMakeExcited(t *testing.T) { 38 | expected := "OMG SO EXCITING!" 39 | actual := MakeExcited("omg so exciting") 40 | if actual != expected { 41 | t.Errorf("Average was incorrect! Expected: %s, Actual: %s", expected, actual) 42 | } 43 | } 44 | ``` -------------------------------------------------------------------------------- /05_toolkit/packages.md: -------------------------------------------------------------------------------- 1 | # Packages 2 | 3 | Packages are directories with one or more Go source files that allow Go to reuse code across a program and across files. 4 | 5 | Every Go file must belong to a package. 6 | 7 | So far, the packages we've seen are `main`, the package we wrote, and `fmt` and `reflect`, which are built into the Go source code. There are [tons of packages](https://golang.org/pkg/) that come out of the box with Go. 8 | 9 | To import packages, you list them at the top of your file either like this: 10 | 11 | ```go 12 | import "fmt" 13 | import "math" 14 | import "reflect" 15 | ``` 16 | 17 | Or more commonly, like this: 18 | 19 | ```go 20 | import ( 21 | "fmt" 22 | "math" 23 | "reflect" 24 | ) 25 | ``` 26 | 27 | Some of the packages that ship with Go are: 28 | 29 | - `strings` - simple functions to manipulate strings 30 | 31 | - Example Methods: `Contains`, `Count`, `Index`, `HasPrefix` 32 | 33 | - `math` - mathematical operations 34 | 35 | - `Pow`, `Abs`, `Ceil`, `Floor` etc 36 | - `isNaN`, `NaN` 37 | 38 | - `io` - handles input/output methods related to the `os` package (like reading 39 | files) 40 | 41 | - Example Methods: `Copy`, `Reader`, `Writer` 42 | 43 | - `os` - methods around operating system functionality 44 | 45 | - Example Methods: `Open`, `Rename`, `CreateFile` 46 | 47 | - `testing` - Go's build in test suite 48 | 49 | - Example Methods: `Skip`, `Run`, `Error` 50 | 51 | - `net/http` - provides http client and server implementations 52 | - Example Methods: `Get`, `Post`, `Handle` 53 | 54 | ## Exported vs Unexported Names 55 | 56 | When you import a package, you can only access that package's public, exported names. In Go, these must start with a capital letter. 57 | 58 | Anything that starts with a lowercase letter is a private method and will NOT be exported. These are only 59 | visible within the same package. 60 | 61 | ```go 62 | fmt.Println() 63 | ``` 64 | 65 | Within the `fmt` package, the function `Println` is exported (starts with a 66 | capital letter) and therefore can be accessed within our `main` package. 67 | 68 | ### Custom Packages 69 | 70 | Until now we've been writing all of our go code in the `main` package and only 71 | using functionality from Go's imported libraries. 72 | 73 | To demonstrate how packages work, let's create a folder called `utils`, and within that create a file called 74 | `math.go` where we will add a few small functions. 75 | 76 | In `math.go` add the following: 77 | 78 | ```go 79 | package utils 80 | 81 | import "fmt" 82 | 83 | func printNum(num int) { 84 | fmt.Println("Current Number: ", num) 85 | } 86 | 87 | func Add(nums ...int) int { 88 | total := 0 89 | for _, v := range nums { 90 | printNum(v) 91 | total += v 92 | } 93 | return total 94 | } 95 | ``` 96 | 97 | And then in our `packages.go` file, lets import and use this package. 98 | 99 | Note that the import name is a path that is relative to the `src` directory. 100 | Often, this will be a github location as your files will live in a github 101 | repository. 102 | 103 | Also note that the file name itself isn't necessary as long as that file lives 104 | within the package directory and has its package declaration at the top of the 105 | file. 106 | 107 | ```go 108 | import { 109 | "fmt" 110 | "path/from/src/to/utils" // => ie: fem-intro-to-go/utils 111 | } 112 | 113 | func calculateImportantData() int { 114 | totalValue := utils.Add(1, 2, 3, 4, 5) 115 | return totalValue 116 | } 117 | 118 | func main() { 119 | total := calculateImportantData() 120 | fmt.Println(total) 121 | } 122 | ``` 123 | -------------------------------------------------------------------------------- /05_toolkit/testing.md: -------------------------------------------------------------------------------- 1 | # Unit Testing 2 | 3 | ## Testing a Method 4 | 5 | Go includes a package, `testing`, that contains all the functions necessary to 6 | run a test suite and command to run those tests, `go test`. 7 | 8 | Test file names must end with `_test.go` and the Go compiler will know to ignore 9 | these unless `go test` is run. 10 | 11 | The test files need to be in the same package as the method they are testing. 12 | 13 | The test name should start with `Test` followed by the name of the 14 | function you are testing. 15 | 16 | The only parameter passed into the test should be `t *testing.T`. 17 | 18 | Note: We will discuss what that `*` means shortly. 19 | 20 | ```go 21 | // average_test.go 22 | 23 | package utils 24 | 25 | import "testing" 26 | 27 | // The test should always accept the same argument, t, of type *testing.T) 28 | func TestAverage(t *testing.T) { 29 | // the value you expect to get from the tested function 30 | expected := 4 31 | actual := utils.average(1, 2, 3) 32 | if actual != expected { 33 | t.Errorf("Average was incorrect! Expected: %d, Actual: %d", expected, actual) 34 | } 35 | } 36 | ``` 37 | 38 | To run tests, use the command `go test` from within the directory where the test 39 | file lives. 40 | 41 | _TRY IT_ 42 | Test The Add Function from our `math.go` file in utils. 43 | -------------------------------------------------------------------------------- /05_toolkit/tools.md: -------------------------------------------------------------------------------- 1 | # Go Tools 2 | 3 | ## Compiling Go Packages 4 | 5 | In the previous Hello World example we used the command `go run ...` to compile and run our 6 | `main.go` program. Let's discuss a few others: 7 | 8 | ## Build & Install 9 | 10 | The two commands `go build` and `go install` will compile all of your go 11 | packages and dependencies and put the executable in the current directory (when run without any flags). 12 | 13 | `go install` will also put the compiled dependencies into the `pkg` directory 14 | within your go workspace. 15 | 16 | `go build` lets you build an executable file locally, which lets you test your 17 | application without having to deploy it immediately. 18 | 19 | ## go fmt 20 | 21 | `go fmt` will reformat your code based on Go standards. This is often run as a 22 | linter, (like on save of a file). 23 | 24 | ## go list 25 | 26 | `go list` will list all of the packages in that current directory. 27 | 28 | ## go vet 29 | 30 | `go vet` runs through your source code and yells about any weird constructs 31 | 32 | It catches small syntax errors that often slip through code review or unit tests - like `fmt.Printf` calls that have misaligned arguments. 33 | 34 | ## go doc 35 | 36 | `go doc` we mentioned earlier, will give you access to local help docs 37 | 38 | ## go get 39 | 40 | In order to install an external package, Go provides the `go get` command. 41 | Unlike installing an `npm` module, in Go you need to specify the location of 42 | where that executable lives. For example, if we want to use the `golint` 43 | package, we would install it like so: 44 | 45 | `go get -u golang.org/x/lint/golint` 46 | 47 | ## go lint 48 | 49 | Go lint is concerned with style mistakes and maintaining a consistent coding style that is miantained at Google. 50 | 51 | Install `golint` using the command above. 52 | 53 | To find out where the library was put, run `go list -f {{.Target}} golang.org/x/lint/golint` 54 | 55 | For `golint` to be used globally, make sure the path you see from the above command is included in your `$PATH` variable. -------------------------------------------------------------------------------- /06_structs/code/exercise_6a_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // // User is a user type 6 | // type User struct { 7 | // ID int 8 | // FirstName, LastName, Email string 9 | // } 10 | 11 | // // Group represents a set of users 12 | // type Group struct { 13 | // role string 14 | // users []User 15 | // newestUser User 16 | // spaceAvailable bool 17 | // } 18 | 19 | // func describeUser(u User) string { 20 | // desc := fmt.Sprintf("Name: %s %s, Email: %s", u.FirstName, u.LastName, u.Email) 21 | // return desc 22 | // } 23 | 24 | // // func describeGroup 25 | // // => "This user group has 19 users. The newest user is Joe Smith. Accepting New Users: true" 26 | 27 | // func describeGroup(g Group) string { 28 | 29 | // if len(g.users) > 2 { 30 | // g.spaceAvailable = false 31 | // } 32 | 33 | // desc := fmt.Sprintf("This user group has %d. The newest user is %s %s. Accepting New Users: %t", len(g.users), g.newestUser.FirstName, g.newestUser.LastName, g.spaceAvailable) 34 | // return desc 35 | // } 36 | 37 | // func main() { 38 | // u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 39 | 40 | // u2 := User{ID: 2, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 41 | 42 | // u3 := User{ID: 2, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 43 | 44 | // g := Group{ 45 | // role: "admin", 46 | // users: []User{u, u2, u3}, 47 | // newestUser: u2, 48 | // spaceAvailable: true, 49 | // } 50 | 51 | // fmt.Println(describeUser(u)) 52 | // fmt.Println(describeGroup(g)) 53 | // fmt.Println(g) 54 | // } 55 | -------------------------------------------------------------------------------- /06_structs/code/structs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // User is a user type 6 | type User struct { 7 | ID int 8 | FirstName, LastName, Email string 9 | } 10 | 11 | // // User is a user type 12 | // type User struct { 13 | // ID int 14 | // FirstName, LastName, Email string 15 | // } 16 | 17 | func main() { 18 | u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 19 | 20 | fmt.Println(u) 21 | } 22 | -------------------------------------------------------------------------------- /06_structs/exercise_6a.md: -------------------------------------------------------------------------------- 1 | # Exercise 6A: Structs 2 | 3 | ## Goals 4 | 5 | - Work with structs 6 | 7 | ## Setup 8 | 9 | - Reference the code discussed in `structs.md` and `code/structs.go` 10 | 11 | - Let's say that in `describeGroup`, we only want to accept new users (represented by the `spaceAvailable` field) if there are fewer than 2 existing users in the group. 12 | 13 | ## Directions 14 | 15 | 1. In the `describeGroup`, update `spaceAvailable` to be `false` if there are 2 or more users. 16 | 17 | 2. Reprint the variable `g` at the end of `main()`. What do you notice? 18 | -------------------------------------------------------------------------------- /06_structs/structs.md: -------------------------------------------------------------------------------- 1 | # Defining a struct 2 | 3 | A `struct` is a data type that contains a collection of fields that act as variables, have defined types and is reusable across your program. 4 | 5 | Think an ES6 Class in JS, or an object of values without keys. 6 | 7 | ```go 8 | type User struct { 9 | ID int 10 | FirstName string 11 | LastName string 12 | Email string 13 | } 14 | ``` 15 | 16 | Another way to write this is: 17 | 18 | ```go 19 | type User struct { 20 | ID int 21 | FirstName, LastName, Email string 22 | } 23 | ``` 24 | 25 | We can now treat the `User` struct as a type of variable and instantiate it with or without default values. 26 | 27 | ```go 28 | var u User 29 | fmt.Pringln(u) 30 | ``` 31 | 32 | What do you notice about the initial values of the variable `u`? 33 | 34 | Let's add values. 35 | 36 | ```go 37 | var u User 38 | u.ID = 1 39 | u.FirstName = "Marilyn" 40 | u.LastName = "Monroe" 41 | u.Email = "marilyn.monroe@gmail.com" 42 | ``` 43 | 44 | Or, more commonly, in shorthand 45 | 46 | ```go 47 | u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 48 | ``` 49 | 50 | 51 | # Reading a Struct 52 | 53 | Then, in order to access the fields, we use dot notation similar to working with 54 | Objects in JS. 55 | 56 | ```go 57 | u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 58 | fmt.Println(u.FirstName) // => "Marilyn" 59 | ``` 60 | 61 | To pass a struct to a function, we simply add the struct name within the 62 | function signature as the type of argument we are passing in. 63 | 64 | ```go 65 | func describeUser(u User) string { 66 | desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.FirstName, u.LastName, u.Email, u.ID) 67 | return desc 68 | } 69 | ``` 70 | 71 | In `main()`, add a few lines to call this function. 72 | 73 | ```go 74 | func main() { 75 | u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 76 | userDescription := describeUser(u) 77 | fmt.Println(userDescription) 78 | } 79 | ``` 80 | 81 | Let's create another struct for a group of users (think `admin` vs `guest`). 82 | 83 | ```go 84 | type Group struct { 85 | role string 86 | users []User 87 | newestUser User 88 | spaceAvailable bool 89 | } 90 | ``` 91 | 92 | Next, create a function that describes the team of users: 93 | 94 | ```go 95 | func describeGroup(g Group) string { 96 | desc := fmt.Sprintf("The %s user group has %d users. Newest user: %s, Accepting New Users: %t", g.role, len(g.users), g.newestUser.FirstName, g.spaceAvailable) 97 | return desc 98 | } 99 | ``` 100 | 101 | In main we can set up instances of our structs and call each function: 102 | 103 | ```go 104 | func main() { 105 | u1 := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 106 | u2 := User{ID: 2, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 107 | 108 | g := Group{role: "admin", users: []User{u1, u2}, newestUser: u2, spaceAvailable: true} 109 | 110 | userDescription := describeUser(u1) 111 | groupDescription := describeGroup(g) 112 | 113 | fmt.Println(userDescription) 114 | fmt.Println(groupDescription) 115 | } 116 | ``` 117 | 118 | ## Accessibility 119 | 120 | Note that like variables and functions, only field names that have a capital 121 | letter are visible outside of the package where the struct is defined. 122 | 123 | ```go 124 | type User struct { 125 | ID int 126 | Email string 127 | FirstName string 128 | LastName string 129 | } 130 | ``` 131 | 132 | ## EXERCISE 6A 133 | `06_structs/exercise_6a` 134 | 135 | 136 | # Mutating Instances Of A Struct 137 | 138 | In Go, unless otherwise indicated, we are passing around a _COPY_ of any variable we reference to existing functions. In the case above, we pass a copy of the variable `g`, and then are asking our program to print the original value of `g`. The original value is still safely untouched in memory. 139 | 140 | In order to permanently modify this variable, we need to use a special kind of variables called a `pointer`. -------------------------------------------------------------------------------- /07_pointers/code/exercise_7a_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // // A User is a user type 6 | // type User struct { 7 | // ID int 8 | // FirstName, LastName, Email string 9 | // } 10 | 11 | // var u = User{ 12 | // ID: 1, 13 | // FirstName: "Marilyn", 14 | // LastName: "Monroe", 15 | // Email: "marilynmonroe@gmail.com", 16 | // } 17 | 18 | // func updateEmail(u *User) { 19 | // u.Email = "newEmail@gmail.com" 20 | // fmt.Println("in update email: ", u.Email) 21 | // } 22 | 23 | // func main() { 24 | // fmt.Println("Pointers!") 25 | // updateEmail(&u) 26 | // fmt.Println("Updated User: ", u) 27 | // } 28 | -------------------------------------------------------------------------------- /07_pointers/code/pointers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var name string 7 | var namePointer *string 8 | 9 | fmt.Println("Name:", name) 10 | fmt.Println("Name *:", namePointer) 11 | } 12 | 13 | // // ****************************************************** 14 | 15 | // func main() { 16 | // var name string = "Beyonce" 17 | // var namePointer *string = &name 18 | // var nameValue = *namePointer 19 | 20 | // fmt.Println("Name:", name) 21 | // fmt.Println("Name *:", namePointer) 22 | // fmt.Println("Name Value:", nameValue) 23 | 24 | // } 25 | 26 | // // ****************************************************** 27 | 28 | // func changeName(n string) { 29 | // n = strings.ToUpper(n) 30 | // } 31 | 32 | // func main() { 33 | // name := "Elvis" 34 | // changeName(name) 35 | // fmt.Println(name) 36 | // } 37 | 38 | // // ****************************************************** 39 | -------------------------------------------------------------------------------- /07_pointers/exercise_7a.md: -------------------------------------------------------------------------------- 1 | ## Exercise 7A: Pointers 2 | 3 | ## Goals 4 | 5 | - Practice navigating between variables and pointers 6 | - Practice building and modifying structs 7 | 8 | ## Setup 9 | 10 | - Create a file called `exercise_7a.go` in the `07_pointers/code` directory 11 | - Start with the setup code snippet below 12 | 13 | ## Directions 14 | 15 | 1. Define an instance of the User struct 16 | 17 | 2. Write a function called `updateEmail` that takes in a `*User` type 18 | 19 | 3. Update the user's email to something new 20 | 21 | 4. Call `updateEmail()` from `main()` and verify the updated email has persisted 22 | 23 | ```go 24 | package main 25 | 26 | import "fmt" 27 | 28 | type User struct { 29 | ID int 30 | FirstName, LastName, Email string 31 | } 32 | 33 | // YOUR CODE HERE 34 | 35 | func main() { 36 | fmt.Println("Pointers!") 37 | } 38 | ``` 39 | 40 | ## Hints 41 | 42 | _HINT_ Check out the [go docs](https://tour.golang.org/moretypes/4) if things feel a little weird 43 | -------------------------------------------------------------------------------- /07_pointers/pointers.md: -------------------------------------------------------------------------------- 1 | # Pointers 2 | 3 | This can be a particularly tricky aspect of Go when coming from a dynamically typed language like JavaScript. 4 | 5 | Review: When defining a variable, that variable gets stored in a location in computer memory, which has a memory address. 6 | 7 | At the base level, a pointer variable in Go is a variable that holds the _memory location_ of that variable, instead of a copy of its value. 8 | 9 | ```go 10 | var name string 11 | var namePointer *string 12 | 13 | fmt.Println(name) 14 | fmt.Println(namePointer) 15 | ``` 16 | 17 | You'll notice here that set to their default values, a normal string variable (`name`) is assigned a value of `""`. 18 | 19 | A pointer string variable (`namePointer`), however, is assigned it's default value of `` in anticipation of a future memory location. 20 | 21 | To assign a variable it's address in memory, you use the `&` 22 | symbol (think "a - at - for address"). 23 | 24 | ```go 25 | var name string 26 | var namePointer *string 27 | 28 | fmt.Println(name) 29 | fmt.Println(namePointer) 30 | fmt.Println(&name) 31 | ``` 32 | 33 | _SLIDE_ 34 | 35 | _TRY IT_ 36 | Set both `name` and `namePointer` to values. 37 | What happens? 38 | What does the error message mean? 39 | How do you assign an address location of the `name` variable to `namePointer` variable instead? 40 | 41 | // SOLUTION 42 | 43 | ```go 44 | var name string = "Beyonce" 45 | var namePointer *string = &name 46 | 47 | fmt.Println(name) 48 | fmt.Println(namePointer) 49 | ``` 50 | 51 | So now we're looking at the _memory address_ of a variable, rather than the value that is stored there. 52 | 53 | To get that value, we need to dig into that address and pull the value out. 54 | 55 | To `read through` a pointer variable to get the actual value, you use `pointer indirection` to "deference" that variable back to its original value. 56 | 57 | ```go 58 | var name string = "Beyonce" 59 | 60 | var namePointer *string = &name 61 | var nameValue = *namePointer 62 | 63 | fmt.Println(name) 64 | fmt.Println(namePointer) 65 | fmt.Println(nameValue) 66 | ``` 67 | 68 | ### To Summarize In Other Words: 69 | 70 | - Pointer types are indicated with a `*` next to the _TYPE NAME_, indicates that variable will POINT TO a memory location. 71 | 72 | - Pointer variable values are visible with a `*` next to the _VARIABLE NAME_. 73 | 74 | - To read through a variable to see the pointer address, use a `&` next to the _VARIABLE NAME_. 75 | 76 | 77 | - ie: `var nameValue = *namePointer` => "Marilyn" 78 | 79 | ## Pass By Value 80 | 81 | Function parameters represent a COPY of of the value that they reference. 82 | 83 | This means that any change made to that variable within the function is modifying the copy, not the underlying value in memory. 84 | 85 | ```go 86 | func changeName(n string) { 87 | n = strings.ToUpper(n) 88 | } 89 | 90 | func main() { 91 | name := "Elvis" 92 | changeName(name) 93 | fmt.Println(name) 94 | } 95 | ``` 96 | 97 | If we did want the `changeName` function to modify the name variable, we need to pass a pointer. 98 | 99 | ```go 100 | func changeName(n *string) { 101 | *n = strings.ToUpper(*n) 102 | } 103 | 104 | func main() { 105 | name := "Elvis" 106 | changeName(&name) 107 | fmt.Println(name) 108 | } 109 | ``` 110 | 111 | ## Pointers And Structs 112 | 113 | As the [go gocs](https://tour.golang.org/moretypes/4) mention, Go gives us a small loophole when working with pointer structs. 114 | 115 | Let's take the following struct: 116 | 117 | ```go 118 | type Coordinates struct { 119 | X, Y float64 120 | } 121 | 122 | var c = Coordinates{x: 120.5, y: 300} 123 | ``` 124 | 125 | To access a field on a struct pointer, we would need syntax that looked like this: 126 | 127 | ```go 128 | func main() { 129 | pointerCoords = &c 130 | (*pointerCoords).X = 200 131 | } 132 | ``` 133 | 134 | The `(*pointerCoords)` bit is a tad cumbersome, so Go allows us to simply reference the pointer without the dereferencing asterisk but knows that behind the scenes we are talking about the pointer struct. 135 | 136 | 137 | -------------------------------------------------------------------------------- /08_errors/code/errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | func isGreaterThanTen(num int) error { 9 | if num < 10 { 10 | return errors.New("something bad happened") 11 | } 12 | return nil 13 | } 14 | 15 | // func openFile() error { 16 | // f, err := os.Open("missingFile.txt") 17 | // if err != nil { 18 | // return err 19 | // } 20 | // defer f.Close() 21 | // return nil 22 | // } 23 | 24 | func main() { 25 | num := 9 26 | err := isGreaterThanTen(num) 27 | if err != nil { 28 | fmt.Println(fmt.Errorf("%d is NOT GREATER THAN TEN", num)) 29 | // panic(err) 30 | // log.Fatalln(err) 31 | } 32 | 33 | // err := openFile() 34 | 35 | // if err != nil { 36 | // fmt.Println(fmt.Errorf("%v", err)) 37 | // } 38 | } 39 | 40 | // TAKE A MINUTE TO REFACTOR THE ABOVE CODE TO SCOPE THE ERROR VARIABLE INTO THE IF BLOCK 41 | 42 | // **************************************************** 43 | 44 | // PANIC & DEFER SLIDE 45 | 46 | // **************************************************** 47 | 48 | // package main 49 | 50 | // import ( 51 | // "fmt" 52 | // ) 53 | 54 | // func doThings() { 55 | // defer fmt.Println("First Line but do this last!") 56 | // defer fmt.Println("Do this second to last!") 57 | // fmt.Println("Things And Stuff should happen first") 58 | // } 59 | 60 | // func main() { 61 | // doThings() 62 | // } 63 | 64 | // **************************************************** 65 | 66 | // RECOVER SLIDE 67 | 68 | // **************************************************** 69 | 70 | // package main 71 | 72 | // import ( 73 | // "fmt" 74 | // ) 75 | 76 | // func doThings() { 77 | // for i := 0; i < 5; i++ { 78 | // fmt.Println(i) 79 | // if i == 2 { 80 | // panic("PANIC!") 81 | // } 82 | // } 83 | // } 84 | 85 | // func main() { 86 | // doThings() 87 | // } 88 | 89 | // **************************************************** 90 | 91 | // package main 92 | 93 | // import ( 94 | // "fmt" 95 | // ) 96 | 97 | // func handlePanic() string { 98 | // return "HANDLING THE PANIC" 99 | // } 100 | 101 | // func recoverFromPanic() { 102 | // // recover() will only return a value if there has been a panic 103 | // if r := recover(); r != nil { 104 | // fmt.Println("We panicked but everything is fine.") 105 | // fmt.Println("Panic instructions received:", r) 106 | // } 107 | // } 108 | 109 | // func doThings() { 110 | // defer recoverFromPanic() 111 | // for i := 0; i < 5; i++ { 112 | // fmt.Println(i) 113 | // if i == 2 { 114 | // panic(handlePanic()) 115 | // } 116 | // } 117 | // } 118 | 119 | // func main() { 120 | // doThings() 121 | // } 122 | -------------------------------------------------------------------------------- /08_errors/errors.md: -------------------------------------------------------------------------------- 1 | # Error Type 2 | 3 | The first type of error we will talk about is the built in `error` type. 4 | 5 | When this occurs, most of the time you will have a game plan for what to do instead and your program can continue to run. 6 | 7 | The built in `error` type is an `interface` type (which will be discussed shortly), with a method called `Error()` that returns the string of the error message itself. 8 | 9 | That interface looks like this: 10 | 11 | ```go 12 | type error interface { 13 | Error() string 14 | } 15 | ``` 16 | 17 | ```go 18 | func isGreaterThanTen(num int) error { 19 | if num < 10 { 20 | return fmt.Errorf("%v is NOT GREATER THAN TEN", num) 21 | } 22 | return nil 23 | } 24 | 25 | func main() { 26 | err := isGreaterThanTen(9) 27 | if err != nil { 28 | log.Println(err) 29 | } else { 30 | fmt.Println("Carry On.") 31 | } 32 | } 33 | ``` 34 | 35 | Here, the `fmt` package will return an error. (Run `go doc fmt.Errorf` to check it out). 36 | 37 | IMPORTANT NOTE: `nil` is an acceptable return value for an `error` type as well. 38 | 39 | NOTE: Log: This is a simple logging package built in to Go. You can format the output similarly to `fmt` which prints it to the console. Log will print the date and time of each logged message. Again, check out `go doc log` for more details. 40 | 41 | _TRY IT_ 42 | Refactor the main function to consolidate the variable `err` declaration and the if check. 43 | 44 | Hint: Check out the section on `if` statements. 45 | 46 | ## Exiting The Program 47 | 48 | If your program encounters an error that should halt execution, you can use `log.Fatal` from the `log` package which will log the error, and then exit the program. 49 | 50 | ```go 51 | func isGreaterThanTen(num int) error { 52 | if num < 10 { 53 | return fmt.Errorf("%d is NOT GREATER THAN TEN", num) 54 | } 55 | return nil 56 | } 57 | 58 | func main() { 59 | err := isGreaterThanTen(9) 60 | if err != nil { 61 | log.Fatalln(err) 62 | } else { 63 | fmt.Println("Carry On.") 64 | } 65 | } 66 | ``` 67 | 68 | ## Error Handling in Go 1.13 69 | 70 | We will cover the newest approach to error handling released in Go 1.13 towards the end of the course 71 | 72 | -------------------------------------------------------------------------------- /08_errors/panic.md: -------------------------------------------------------------------------------- 1 | # Panic 2 | 3 | So far, you have probably seen your program `panic()` if it can't execute the code because of a syntax error at run time. 4 | 5 | These unpredictable errors occur without explicit instructions. Examples include: 6 | 7 | - Running out of memory. 8 | - A bad url endpoint 9 | - Trying to divide by 0 10 | 11 | A panic will cause the program to stop execution, unless you've included some fail safes within your code. 12 | 13 | Let's talk about what happens when a program Panics. 14 | 15 | ## Panic & Defer 16 | 17 | Go has a few built in helper functions that help guide a go program to behave a certain way: 18 | 19 | - `defer`: executes a line of code last within a function 20 | - `panic`: called during a run time error, halts execution of the program 21 | - `recover`: tells go what to do after a panic 22 | 23 | ### Defer 24 | 25 | Waits until everything else within a function is complete before firing. 26 | 27 | Useful for things like closing a file after doing something with the OS. 28 | 29 | Multiple defers are executed in a `LIFO` order. 30 | 31 | ```go 32 | func doThings() { 33 | defer fmt.Println("First Line but do this last!") 34 | defer fmt.Println("Do this second to last!") 35 | fmt.Println("Things And Stuff should happen first") 36 | } 37 | 38 | func main() { 39 | doThings() 40 | } 41 | ``` 42 | 43 | An example use case would be when opening and closing a file. 44 | 45 | ```go 46 | f, _ := os.Open(filename) 47 | defer f.Close() 48 | 49 | // a bunch of other code you want to run once you've opened the file 50 | ``` 51 | 52 | This is helpful because it keeps two functions that are closely related physically close to each other for readability. 53 | 54 | Note: Deferred functions run last, but they are also run _even if_ a runtime panic occurs. 55 | 56 | ### Panic/Recover 57 | 58 | `Panic` will be called during a run time error and fatally kill the execution of a 59 | program. 60 | 61 | `Recover` will tell Go what to do when that happens, returning what was passed to `panic`. 62 | 63 | Note that in order for recover to do its job, it _must_ be paired with `defer`, which will fire even after a `panic`, otherwise `panic` completely shuts down the execution of a program. 64 | 65 | ```go 66 | func doThings() { 67 | for i := 0; i < 5; i++ { 68 | fmt.Println(i) 69 | if i == 2 { 70 | panic("PANIC!") 71 | } 72 | } 73 | } 74 | 75 | func main() { 76 | doThings() 77 | } 78 | ``` 79 | 80 | With the above code, once we hit the `panic()` function, our program will stop executing. 81 | 82 | Adding a `recover()` cleanup function will tell our program what to do when this happens. 83 | 84 | ```go 85 | func handlePanic() string { 86 | return "HANDLING THE PANIC" 87 | } 88 | 89 | func recoverFromPanic() { 90 | // recover() will only return a value if there has been a panic 91 | if r:= recover(); r != nil { 92 | fmt.Println("We panicked but everything is fine.") 93 | fmt.Println("Panic instructions received:", r) 94 | } 95 | } 96 | 97 | func doThings() { 98 | defer recoverFromPanic() 99 | for i := 0; i < 5; i++ { 100 | fmt.Println(i) 101 | if i == 2 { 102 | panic(handlePanic()) 103 | } 104 | } 105 | } 106 | 107 | func main() { 108 | doThings() 109 | } 110 | ``` 111 | 112 | > _TRY IT_ 113 | > Comment out the defer function 114 | > What happens? 115 | -------------------------------------------------------------------------------- /09_methods/code/exercise_9a_solution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import "fmt" 4 | 5 | // // User is a user type 6 | // type User struct { 7 | // ID int 8 | // FirstName, LastName, Email string 9 | // } 10 | 11 | // // Group represents a set of users 12 | // type Group struct { 13 | // role string 14 | // users []User 15 | // newestUser User 16 | // spaceAvailable bool 17 | // } 18 | 19 | // func (u *User) describe() string { 20 | // desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.FirstName, u.LastName, u.Email, u.ID) 21 | // return desc 22 | // } 23 | 24 | // func (g *Group) describe() string { 25 | // if len(g.users) > 2 { 26 | // g.spaceAvailable = false 27 | // } 28 | 29 | // desc := fmt.Sprintf("This user group has %d. The newest user is %s %s. Accepting New Users: %t", len(g.users), g.newestUser.FirstName, g.newestUser.LastName, g.spaceAvailable) 30 | // return desc 31 | 32 | // } 33 | 34 | // func main() { 35 | // u := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 36 | 37 | // u2 := User{ID: 2, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 38 | 39 | // u3 := User{ID: 2, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 40 | 41 | // g := Group{ 42 | // role: "admin", 43 | // users: []User{u, u2, u3}, 44 | // newestUser: u2, 45 | // spaceAvailable: true, 46 | // } 47 | 48 | // fmt.Println(g.describe()) 49 | // fmt.Println(u.describe()) 50 | // fmt.Println(g) 51 | // } 52 | -------------------------------------------------------------------------------- /09_methods/code/methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // User is a user type 6 | type User struct { 7 | ID int 8 | FirstName, LastName, Email string 9 | } 10 | 11 | func describeUser(u User) string { 12 | desc := fmt.Sprintf("Name: %s %s, Email: %s", u.FirstName, u.LastName, u.Email) 13 | return desc 14 | } 15 | 16 | func main() { 17 | user := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 18 | 19 | desc := describeUser(user) 20 | fmt.Println(desc) 21 | } 22 | -------------------------------------------------------------------------------- /09_methods/exercise_9a.md: -------------------------------------------------------------------------------- 1 | # Exercise 9: Methods 2 | 3 | ## Goals 4 | 5 | - Practice writing a method 6 | 7 | ## Setup 8 | 9 | - Revisit the Structs lesson (`06_structs/structs.md`) and use the `06_structs/exercise_6a.md` exercise for reference. 10 | 11 | - Copy the code from the solution of `06_structs/exercise_6a` into a new file called `exercise_9a.go` as a starting point. 12 | 13 | ## Directions 14 | 15 | 1. Refactor the `describeUser` code to be a method (this should be a repetition of what was just completed in the course). 16 | 17 | 2. Modify the `describeGroup` function to be a method called `describe()` that receives a `Group` type. 18 | 19 | 3. Modify the main function to reflect those changes 20 | 21 | -------------------------------------------------------------------------------- /09_methods/methods.md: -------------------------------------------------------------------------------- 1 | ## Methods 2 | 3 | Similar to structs and variables, functions can also be bound to types. 4 | 5 | Let's revisit the `describeUser` function , and User struct, from a few examples ago: 6 | 7 | ```go 8 | type User struct { 9 | ID int 10 | FirstName string 11 | LastName string 12 | Email string 13 | } 14 | 15 | func describeUser(u User) string { 16 | desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.firstName, u.lastName, u.email, u.ID) 17 | return desc 18 | } 19 | 20 | func main() { 21 | user := User{ID: 1, firstName: "Marilyn", lastName: "Monroe", email: "marilyn.monroe@gmail.com"} 22 | desc := describeUser(user) 23 | } 24 | ``` 25 | 26 | In the function signature above, we name our function `decribeUser()`, and pass in an explicit argument of type `User`, which must return a `string`. 27 | 28 | To refactor this into a method, instead of passing a `User` struct as an argument, we insert it as a `receiver` between the `func` keyword and the name of our slightly renamed `describe()` function. 29 | 30 | This tells Go that when the `describe()` function receives (is called on) a struct with the shape `User`, it should execute the code within the curly braces. 31 | 32 | We can then call the method a little differently: 33 | 34 | ```go 35 | func (u *User) describe() string { 36 | desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.firstName, u.lastName, u.email, u.ID) 37 | return desc 38 | } 39 | 40 | func main() { 41 | u := User{ID: 1, firstName: "Marilyn", lastName: "Monroe", email: "marilyn.monroe@gmail.com"} 42 | desc := u.describe() 43 | } 44 | ``` 45 | 46 | Note that methods DO NOT require the `&` operator to permanently modify the struct being acted on- Go automatically passes a pointer to a method call. 47 | 48 | ## Advantages 49 | 50 | - Encapsulation 51 | - Polymorphism - two different types can have a method of the same name (as seen 52 | above) 53 | - Functions that control/modify state 54 | 55 | ## Exercise 9 56 | `09_methods/exercise_9a.md` 57 | -------------------------------------------------------------------------------- /10_interfaces/code/interfaces.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Add a Describer interface 6 | 7 | // User is a single user type 8 | type User struct { 9 | ID int 10 | FirstName, LastName, Email string 11 | } 12 | 13 | // Group is a group of Users 14 | type Group struct { 15 | role string 16 | users []User 17 | newestUser User 18 | spaceAvailable bool 19 | } 20 | 21 | // These two structs have different implementations of the `describe()` method. 22 | 23 | func (u *User) describe() string { 24 | desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.FirstName, u.LastName, u.Email, u.ID) 25 | return desc 26 | } 27 | 28 | func (g *Group) describe() string { 29 | desc := fmt.Sprintf("The %s user group has %d users. Newest user: %s, Accepting New Users: %t", g.role, len(g.users), g.newestUser.FirstName, g.spaceAvailable) 30 | return desc 31 | } 32 | 33 | // Create a function that doesn't care what type you pass in as long as the type "satisfies the interface" 34 | 35 | func main() { 36 | u1 := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 37 | u2 := User{ID: 1, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 38 | g := Group{role: "admin", users: []User{u1, u2}, newestUser: u2, spaceAvailable: true} 39 | describeUser := u1.describe() 40 | describeGroup := g.describe() 41 | fmt.Println(describeUser) 42 | fmt.Println(describeGroup) 43 | } 44 | -------------------------------------------------------------------------------- /10_interfaces/interfaces.md: -------------------------------------------------------------------------------- 1 | # Interfaces 2 | 3 | If you think of structs as a set of properties that define a type, interfaces can be thought of as a set of methods that define a type. 4 | 5 | In other words, an interface contains a list of function signatures, describing the behavior of other types. 6 | 7 | ```go 8 | type Describer interface { 9 | describe() string 10 | } 11 | ``` 12 | 13 | Once again we start with the `type` keyword, followed by the name of the 14 | interface, and then the actual keyword `interface`. 15 | 16 | Note that the convention is to name an interface with the "job" it is doing - often ending in `er`. 17 | 18 | Within the curly braces we list a set of methods that are associated with our interface type and the type those methods should return. 19 | 20 | Our interface `Describer` includes any type that has a method named `describe()`, which in our program includes `User` and `Group`. Any type that defines this method is said to `satisfy the Describer interface`. 21 | 22 | 23 | ```go 24 | func main() { 25 | u1 := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 26 | u2 := User{ID: 1, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 27 | 28 | g := Group{role: "admin", users: []User{u1, u2}, newestUser: u2, spaceAvailable: true} 29 | 30 | describeUser := u1.describe() 31 | describeGroup := g.describe() 32 | 33 | fmt.Println(describeUser) 34 | fmt.Println(describeGroup) 35 | } 36 | 37 | ``` 38 | 39 | You can also use the empty interface type to indicate that Go should accept 40 | anything. 41 | 42 | ```go 43 | interface{} 44 | ``` 45 | 46 | ```go 47 | func DoSomething(v interface{}) { 48 | // This function will take anything as a parameter. 49 | } 50 | ``` 51 | 52 | Once again, our entire program should now look like this: 53 | 54 | ```go 55 | package main 56 | 57 | import "fmt" 58 | 59 | // Describer prints out a entity description 60 | type Describer interface { 61 | describe() string 62 | } 63 | 64 | // User is a single user type 65 | type User struct { 66 | ID int 67 | FirstName, LastName, Email string 68 | } 69 | 70 | // Group is a group of Users 71 | type Group struct { 72 | role string 73 | users []User 74 | newestUser User 75 | spaceAvailable bool 76 | } 77 | 78 | func (u *User) describe() string { 79 | desc := fmt.Sprintf("Name: %s %s, Email: %s, ID: %d", u.FirstName, u.LastName, u.Email, u.ID) 80 | return desc 81 | } 82 | 83 | func (g *Group) describe() string { 84 | desc := fmt.Sprintf("The %s user group has %d users. Newest user: %s, Accepting New Users: %t", g.role, len(g.users), g.newestUser.FirstName, g.spaceAvailable) 85 | return desc 86 | } 87 | 88 | // DoTheDescribing can be implemented on any type that has a describe method 89 | func DoTheDescribing(d Describer) string { 90 | return d.describe() 91 | } 92 | 93 | func main() { 94 | u1 := User{ID: 1, FirstName: "Marilyn", LastName: "Monroe", Email: "marilyn.monroe@gmail.com"} 95 | u2 := User{ID: 1, FirstName: "Humphrey", LastName: "Bogart", Email: "humphrey.bogart@gmail.com"} 96 | g := Group{role: "admin", users: []User{u1, u2}, newestUser: u2, spaceAvailable: true} 97 | 98 | describeUser := u1.describe() 99 | describeGroup := g.describe() 100 | 101 | userDescribeWithIface := DoTheDescribing(&u1) 102 | groupDescribeWithIface := DoTheDescribing(&g) 103 | 104 | fmt.Println(describeUser) 105 | fmt.Println(describeGroup) 106 | 107 | fmt.Println(userDescribeWithIface) 108 | fmt.Println(groupDescribeWithIface) 109 | } 110 | ``` 111 | -------------------------------------------------------------------------------- /11_server/code/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Building a web server") 7 | } 8 | -------------------------------------------------------------------------------- /11_server/code/server_final.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // "html/template" 6 | // "log" 7 | // "net/http" 8 | // ) 9 | 10 | // func home(w http.ResponseWriter, r *http.Request) { 11 | // fmt.Println("Home!") 12 | // } 13 | 14 | // // Todo is a todo with a title and content 15 | // type Todo struct { 16 | // Title string 17 | // Content string 18 | // } 19 | 20 | // //PageVariables are variables sent to the html template 21 | // type PageVariables struct { 22 | // PageTitle string 23 | // PageTodos []Todo 24 | // } 25 | 26 | // var todos []Todo 27 | 28 | // func getTodos(w http.ResponseWriter, r *http.Request) { 29 | // pageVariables := PageVariables{ 30 | // PageTitle: "Get Todos", 31 | // PageTodos: todos, 32 | // } 33 | // t, err := template.ParseFiles("todos.html") 34 | 35 | // if err != nil { 36 | // http.Error(w, err.Error(), http.StatusBadRequest) 37 | // log.Print("Template parsing error:", err) 38 | // } 39 | 40 | // err = t.Execute(w, pageVariables) 41 | // } 42 | 43 | // func addTodo(w http.ResponseWriter, r *http.Request) { 44 | // err := r.ParseForm() 45 | // if err != nil { 46 | // http.Error(w, err.Error(), http.StatusBadRequest) 47 | // log.Print("Request parsing error: ", err) 48 | // } 49 | 50 | // todo := Todo{ 51 | // Title: r.FormValue("title"), 52 | // Content: r.FormValue("content"), 53 | // } 54 | 55 | // todos = append(todos, todo) 56 | // log.Print(todos) 57 | // http.Redirect(w, r, "/todos/", http.StatusSeeOther) 58 | // } 59 | 60 | // func main() { 61 | // http.HandleFunc("/", home) 62 | // http.HandleFunc("/todos/", getTodos) 63 | // http.HandleFunc("/add-todo/", addTodo) 64 | // fmt.Println("Server is running on port :8080") 65 | // log.Fatal(http.ListenAndServe(":8080", nil)) 66 | 67 | // } 68 | -------------------------------------------------------------------------------- /11_server/exercise_11a.md: -------------------------------------------------------------------------------- 1 | # Exercise 11A: Build A Web Server 2 | 3 | ## Goals 4 | 5 | - Practice creating http routes and handlers 6 | 7 | ## Setup 8 | 9 | - Create a file called `exercise_11a.go` in the `11_server/code` directory. 10 | 11 | - Feel free to copy the template from `11_server/code/server.go` as a starting place. 12 | 13 | ## Directions 14 | 15 | - This will be a code-along type of exercise so instructions will be verbal as we work through the examples. 16 | -------------------------------------------------------------------------------- /11_server/server.md: -------------------------------------------------------------------------------- 1 | # Routes 2 | 3 | When you write a Go webapp, you need to pick web "middleware" and a multiplexer (mux) to handle routing. If you're used to things like Ruby on Rails or Django, they come with their own multiplexer built in. Also known as a router or URL router. 4 | 5 | To set up routes in Go, we will start by working with the `net/http` package that ships with Go. This package provides the needed interfaces that allow us to read and write to the browser. 6 | 7 | As a sidenote, there are many different multiplex packages to deal with http requests in Go. Their job is to match the incoming URL of each request, against a list of registered patterns that you have defined. 8 | 9 | Think: Routes in react. If your react code sees the `/` route, it knows to load a certain set of components. 10 | 11 | ServeMux is the HTTP request multiplexer that ships with the `http` package found in go source code. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL. 12 | 13 | That being said, it's also touted as a router that contains a certain amount of magic in its routing decisions depending on what the exact pattern it's matching contains. 14 | 15 | An example of another common library used is `github.com/gorilla/mux`. 16 | 17 | ## Defining Routes 18 | 19 | Routes are coralled using the `http` package, and the method `HandlFunc`. 20 | 21 | ```go 22 | func main() { 23 | http.HandleFunc("/", home) 24 | } 25 | ``` 26 | 27 | Here in our `main` function, `HandleFunc` takes two arguments - the filepath to watch for, and which handler method to call when that url is intercepted. 28 | 29 | Before we write out the `home` route handler, let's finish spinning up our web server by adding a few extra lines in `main()`. 30 | 31 | ```go 32 | func main() { 33 | http.HandleFunc("/", home) 34 | fmt.Println("Server is running on port 8080") 35 | err := http.ListenAndServe("localhost:8080", nil) 36 | if err != nil { 37 | return fmt.Errorf("Error firing up server: %v", err) 38 | } 39 | } 40 | ``` 41 | 42 | ## Route Handlers 43 | 44 | Route handlers are functions that tell your program what to do when an http request matches a particular pattern. 45 | 46 | ```go 47 | func home(w http.ResponseWriter, r *http.Request) { 48 | fmt.Fprintf(w, "

Hello World

") 49 | } 50 | ``` 51 | 52 | > Try It 53 | > Add another route to an about page that renders different HTML. 54 | 55 | ```go 56 | package main 57 | 58 | import ( 59 | "fmt" 60 | "log" 61 | "net/http" 62 | ) 63 | 64 | func main() { 65 | http.HandleFunc("/", home) 66 | http.HandleFunc("/about", about) 67 | fmt.Println("Server is running on port 8080") 68 | log.Fatal(http.ListenAndServe(":8080", nil)) 69 | } 70 | 71 | func home(w http.ResponseWriter, r *http.Request) { 72 | fmt.Fprintf(w, "

Hello World

") 73 | } 74 | 75 | func about(w http.ResponseWriter, r *http.Request) { 76 | fmt.Fprintf(w, "

About

") 77 | } 78 | ``` 79 | 80 | ## Structs & JSON 81 | 82 | As front end developers, we are used to working with JSON objecst when we make HTTP requests. 83 | 84 | When working with a struct in Go, we can tell our program what we want our JSON too look like, and then encode/decode it (read: stringify/parse) back and forth into Go. 85 | 86 | Let's revisit our User struct. 87 | 88 | In order to turn this struct into a JSON object, we can use the `enconding/json` package that ships with Go. 89 | 90 | ```go 91 | import "encoding/json" 92 | 93 | type User struct { 94 | ID int 95 | FirstName string 96 | LastName string 97 | Email string 98 | } 99 | 100 | func main() { 101 | u := User{ 102 | ID: 1, 103 | FirstName: "Marilyn", 104 | LastName: "Monroe", 105 | Email: "marilyn.monroe@gmail.com", 106 | } 107 | 108 | data, _ := json.Marshal(u) 109 | fmt.Println(string(data)) 110 | } 111 | ``` 112 | 113 | When you print this out, the structure is ALMOST there, but the formatting isn't conventional JSON. 114 | 115 | To modify what we want those keys to look like , we can add an additional field to our struct called a `field tag`, to tell our Struct "Hey, if you're being formatted into json, use this guy instead". 116 | 117 | ```go 118 | type User struct { 119 | ID int `json:"id"` 120 | FirstName string `json:"firstName"` 121 | LastName string `json:"lastName"` 122 | Email string `json:"emailAddress"` 123 | } 124 | ``` 125 | -------------------------------------------------------------------------------- /12_api/code/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // BaseURL is the base endpoint for the star wars API 8 | const BaseURL = "https://swapi.dev/api/" 9 | 10 | func main() { 11 | fmt.Println(BaseURL) 12 | } 13 | -------------------------------------------------------------------------------- /12_api/code/api_final.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "encoding/json" 5 | // "fmt" 6 | // "io/ioutil" 7 | // "log" 8 | // "net/http" 9 | // ) 10 | 11 | // // BaseURL is the base endpoint for the star wars API 12 | // const BaseURL = "https://swapi.dev/api/" 13 | 14 | // // Planet is a planet type 15 | // type Planet struct { 16 | // Name string `json:"name"` 17 | // Population string `json:"population"` 18 | // Terrain string `json:"terrain"` 19 | // } 20 | 21 | // // Person is a person type 22 | // type Person struct { 23 | // Name string `json:"name"` 24 | // HomeworldURL string `json:"homeworld"` 25 | // Homeworld Planet 26 | // } 27 | 28 | // func (p *Person) getHomeworld() { 29 | // res, err := http.Get(p.HomeworldURL) 30 | // if err != nil { 31 | // log.Print("Error fetching homeworld", err) 32 | // } 33 | 34 | // var bytes []byte 35 | // if bytes, err = ioutil.ReadAll(res.Body); err != nil { 36 | // log.Print("Error reading response body", err) 37 | // } 38 | 39 | // json.Unmarshal(bytes, &p.Homeworld) 40 | // } 41 | 42 | // // AllPeople is a collection of Person types 43 | // type AllPeople struct { 44 | // People []Person `json:"results"` 45 | // } 46 | 47 | // func getPeople(w http.ResponseWriter, r *http.Request) { 48 | // res, err := http.Get(BaseURL + "people") 49 | 50 | // if err != nil { 51 | // http.Error(w, err.Error(), http.StatusBadRequest) 52 | // log.Print("Failed to request star wars people") 53 | // } 54 | // fmt.Println(res) 55 | 56 | // bytes, err := ioutil.ReadAll(res.Body) 57 | 58 | // if err != nil { 59 | // http.Error(w, err.Error(), http.StatusBadRequest) 60 | // log.Print("Failed to parse request body") 61 | // } 62 | 63 | // var people AllPeople 64 | 65 | // fmt.Println(string(bytes)) 66 | 67 | // if err := json.Unmarshal(bytes, &people); err != nil { 68 | // fmt.Println("Error parsing json", err) 69 | // } 70 | 71 | // fmt.Println(people) 72 | 73 | // for _, pers := range people.People { 74 | // pers.getHomeworld() 75 | // fmt.Println(pers) 76 | // } 77 | 78 | // } 79 | // func main() { 80 | // http.HandleFunc("/people", getPeople) 81 | // fmt.Println("Serving on :8080") 82 | // log.Fatal(http.ListenAndServe(":8080", nil)) 83 | // } 84 | -------------------------------------------------------------------------------- /12_api/exercise_12a.md: -------------------------------------------------------------------------------- 1 | # Exercise 12A: Hit an External API 2 | 3 | ## Goals 4 | 5 | - Practice creating http routes and handlers with external API endpoints 6 | 7 | ## Setup 8 | 9 | - Create a file called `exercise_12a.go` in the `12_api/code` directory. 10 | 11 | - Feel free to copy the template from `11_server/code/api.go` as a starting place. 12 | 13 | ## Directions 14 | 15 | - This will be a code-along type of exercise so instructions will be verbal as we work through the examples. 16 | -------------------------------------------------------------------------------- /13_concurrency/code/concurrency.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func say(s string) { 9 | for i := 0; i < 3; i++ { 10 | fmt.Println(s) 11 | time.Sleep(time.Millisecond * 300) 12 | } 13 | } 14 | 15 | func main() { 16 | say("Hello") 17 | say("There") 18 | } 19 | -------------------------------------------------------------------------------- /13_concurrency/code/concurrency_final.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "fmt" 5 | // "sync" 6 | // "time" 7 | // ) 8 | 9 | // var wg sync.WaitGroup 10 | 11 | // func handlePanic() { 12 | // if r := recover(); r != nil { 13 | // fmt.Println("PANIC") 14 | // } 15 | // } 16 | // func printStuff() { 17 | // defer wg.Done() 18 | // defer handlePanic() 19 | // for i := 0; i < 3; i++ { 20 | // fmt.Println(i) 21 | // time.Sleep(time.Millisecond * 300) 22 | // } 23 | // } 24 | 25 | // func main() { 26 | // wg.Add(1) 27 | // go printStuff() 28 | // wg.Wait() 29 | // } 30 | -------------------------------------------------------------------------------- /13_concurrency/concurrency.md: -------------------------------------------------------------------------------- 1 | # Concurrency 2 | 3 | ## What is Concurrency in Go? 4 | 5 | ## Sync 6 | 7 | Goroutine: From [the docs](https://tour.golang.org/concurrency/1), a goroutine is a lightweight threat managed by the Go runtime. 8 | 9 | A goroutine is indicated by adding the keyword `go` before the name of a function. 10 | 11 | This will tell Go to fire up a new goroutine running that function on a separate thread. 12 | 13 | ```go 14 | import ( 15 | "time" 16 | "fmt" 17 | ) 18 | 19 | func say(s string) { 20 | for i := 0; i < 3; i++ { 21 | fmt.Println(s) 22 | time.Sleep(time.Millisecond*100) 23 | } 24 | } 25 | 26 | func main() { 27 | go say("Hello") 28 | say("There") 29 | } 30 | ``` 31 | 32 | If all of the code within a function is a go routine: 33 | 34 | ```go 35 | func main() { 36 | go say("Hello") 37 | go say("There") 38 | } 39 | ``` 40 | 41 | Everything will be non blocking, and nothing will finish execution. In order to fix this, we need to synchronize our goroutines, using the package [sync](https://golang.org/pkg/sync/). 42 | 43 | To start, create a variable that sets up a `WaitGroup` - meaning a set of go routines you want to execute to completion before moving forward in your program. 44 | 45 | ```go 46 | package main 47 | 48 | import ( 49 | "fmt" 50 | "time" 51 | "sync" 52 | ) 53 | 54 | var wg sync.WaitGroup 55 | 56 | func handlePanic() { 57 | if r := recover(); r != nil { 58 | fmt.Println("We panicked! But its fine. Message received: ", r) 59 | } 60 | } 61 | 62 | func printStuff(s string) { 63 | // Decrement the wait group counter 64 | // Use defer so that if the function panics we aren't waiting forever 65 | // Also figure out what to do if the program panics 66 | defer wg.Done() 67 | defer handlePanic() 68 | 69 | for i := 0; i < 3; i++ { 70 | fmt.Println(i) 71 | time.Sleep(time.Millisecond * 100) 72 | } 73 | } 74 | 75 | func main() { 76 | // Increment the wait group counter 77 | wg.Add(1) 78 | // Launch a goroutine 79 | go printStuff() 80 | // Increment the wait group counter 81 | wg.Add(1) 82 | 83 | go printStuff() 84 | // Wait for the waitgroup counter to be 0 before continuing 85 | wg.Wait() 86 | } 87 | ``` 88 | 89 | Here, we're defining a variable that represents our WaitGroup. The `Add()` 90 | function takes an integer and adds it to a behind the scenes counter. If the 91 | counter becomes zero, all goroutines are released and the program will continue. 92 | 93 | ## Channels 94 | 95 | Send and receive values between your goroutines. 96 | 97 | Create a channel but using the `make()` function, and the `chan` keyword, 98 | including the type declaration. 99 | 100 | ```go 101 | myChannel := make(chan int) 102 | ``` 103 | 104 | Then, fire off a couple goroutines form which you are expecting values. 105 | 106 | ```go 107 | package main 108 | 109 | import "fmt" 110 | 111 | func mathThings(c chan int, someValue int) { 112 | // From right to left, calculate the value, send it to the channel 113 | c <- someValue * 5 114 | } 115 | 116 | func main() { 117 | // Instantiate a new channel 118 | resultChan := make(chan int) 119 | 120 | // Kick off a couple go routines 121 | // Notice that we don't need the WaitGroup here, because channels are blocking. 122 | // Our channel is expecting a value back so it will block until it finishes execution 123 | go mathThings(resultChan, 5) 124 | go mathThings(resultChan, 3) 125 | 126 | // Store whatever we get back from our channel, in a var 127 | num1 := <-resultChan 128 | num2 := <-resultChan 129 | // or num1, num2 := <-resultChan, <-resultChan 130 | fmt.Println(num1, num2) 131 | } 132 | ``` 133 | 134 | We can shorten this up a bit by iterating over our channel, adding back in 135 | WaitGroups, and including a buffer value when instantiating our channel: 136 | 137 | ```go 138 | package main 139 | 140 | import 141 | "fmt" 142 | ) 143 | 144 | var wg sync.WaitGroup 145 | 146 | func mathThings(c chan int, someValue int) { 147 | defer wg.Done() 148 | c <- someValue * 5 149 | } 150 | 151 | func main() { 152 | // second argument is the buffer to tell our channel how many times we plan on using it 153 | resultChan := make(chan int, 10) 154 | 155 | for i := 0; i < 10; i++ { 156 | wg.Add(1) 157 | go mathThings(resultChan, i) 158 | } 159 | 160 | // Wait for all goroutines to complete before closing the channel 161 | wg.Wait() 162 | 163 | // Close wont wait for the goroutines to be done without additional synchronization 164 | close(resultChan) 165 | 166 | 167 | for item := range resultChan { 168 | fmt.Println(item) 169 | } 170 | } 171 | ``` 172 | -------------------------------------------------------------------------------- /14_future_go/code/errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func openFile() error { 9 | f, err := os.Open("missingFile.txt") 10 | if err != nil { 11 | return err 12 | } 13 | defer f.Close() 14 | return nil 15 | } 16 | 17 | func main() { 18 | err := openFile() 19 | 20 | if err != nil { 21 | fmt.Println(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /14_future_go/code/errors_final.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // import ( 4 | // "errors" 5 | // "fmt" 6 | // "os" 7 | // ) 8 | 9 | // func openFile() error { 10 | // f, err := os.Open("missingFile.txt") 11 | // if err != nil { 12 | // unwrapped := errors.Unwrap(fmt.Errorf("File is Missing: %w", err)) 13 | // fmt.Println(unwrapped) 14 | // return unwrapped 15 | // } 16 | // defer f.Close() 17 | // return nil 18 | // } 19 | 20 | // func main() { 21 | // err := openFile() 22 | 23 | // if err != nil { 24 | // if errors.Is(err, os.ErrExist) { 25 | // fmt.Println("File doesn't exist") 26 | // } else { 27 | // fmt.Println("something else happened") 28 | // } 29 | // fmt.Println(err) 30 | // } 31 | // } 32 | -------------------------------------------------------------------------------- /14_future_go/future.md: -------------------------------------------------------------------------------- 1 | # GO 2.0 2 | 3 | As of the latest survey on what GO developers struggle with most, the proposal for Go 2.0 includes changes to the following aspects of the language: 4 | 5 | 1. Package management (modules) 6 | 2. Generics 7 | 3. Error Handling 8 | 9 | ## Package Management & Modules 10 | - Think npm modules scoped to a project rather than all dependencies live in the same workspace all the time. 11 | 12 | ## Generics 13 | - 14 | 15 | ## Error Wrapping 16 | 17 | Check out the `errors.go` file in this directory which has a code snippet from our earlier discussion on errors. 18 | 19 | In this version of error handling, we get the message: 20 | `open notes.txt: no such file or directory`, but we are missing the type of erorr or any kind of stack trace indicating where this error came from. 21 | 22 | A common approach is to manually wrap the error in a larger structure providing additional information, like a timestamp, or a string indicating what happened. 23 | 24 | ```go 25 | type WrappedError struct { 26 | err error 27 | time time.Time 28 | } 29 | 30 | func wrap(err Error) *WrappedError { 31 | newErr := &WrappedError{err: err, time: time.Now()} 32 | return newErr 33 | } 34 | 35 | // other code 36 | if err != nil { 37 | return wrap(err) 38 | } 39 | ``` 40 | 41 | Downsides to this include: 42 | - Inconsistencies among various approaches to wrapping bewteen developers 43 | - Having to hand rolling any useful information. 44 | - Inconsistencies in the resulting type assertions 45 | 46 | Go 2.0 is proposing the ability to unwrap errors without having to manually write your own functions around it by introducing the `Wrapper` interface. 47 | 48 | ```go 49 | type Wrapper interface { 50 | // Returns the next error in the error chain, or nil 51 | Unwrap() error 52 | } 53 | ``` 54 | 55 | All types that satisfy the `Wrapper` interface will have a layer of error inspection. 56 | 57 | Take our WrappedError example above. 58 | 59 | ```go 60 | func (err *WrappedError) Unwrap() error { 61 | return err.err 62 | } 63 | ``` 64 | 65 | Let's look at some docs. 66 | 67 | - https://golang.org/pkg/errors/ 68 | - https://golang.org/doc/go1.13#error_wrapping 69 | 70 | ```go 71 | package main 72 | 73 | import ( 74 | "errors" 75 | "fmt" 76 | "os" 77 | ) 78 | 79 | func openFile() error { 80 | f, err := os.Open("missingFile.txt") 81 | if err != nil { 82 | // return err 83 | unwrapped := errors.Unwrap(fmt.Errorf("This File Is Missing: %w", err)) 84 | fmt.Println("unwrapped:", unwrapped) 85 | return unwrapped 86 | } 87 | defer f.Close() 88 | return nil 89 | } 90 | 91 | func main() { 92 | err := openFile() 93 | fmt.Println(err) 94 | if err != nil { 95 | if errors.Is(err, os.ErrNotExist) { 96 | fmt.Println("File Doesnt Exist") 97 | } else { 98 | fmt.Println("Unknown error but its broken") 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | ## Resources 105 | - https://golang.org/doc/go1.13#error_wrapping 106 | - https://github.com/golang/go/issues/29934 107 | - https://golang.org/pkg/errors/ 108 | - https://github.com/golang/go/wiki/ErrorValueFAQ 109 | - https://ieftimov.com/post/deep-dive-in-upcoming-go-error-inspection-changes/ 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go for JavaScript Developers 2 | 3 | Prepared for Frontend Masters 4 | September, 2019 5 | 6 | ## How To Use This Repo 7 | 8 | Welcome to [Go for JavaScript Developers][go]! 9 | 10 | The master branch of this repo is a starting point that is meant to go along with a series of [slides][slides]. Each chapter contains markdown files that include instructions for the exercise as well as teaching materials. All Go files are located under the code/ directory of each chapter that include both teaching materials and solutions to the exercises (if applicable). 11 | 12 | ## Setup 13 | 14 | To take full advantage of the examples and exercises in this repo, make sure to [download and install Go][godownload]. Additional instructions for downloading are located in the course. 15 | 16 | ## Errata 17 | 18 | - The URL for the Star Wars API has been updated to https://swapi.dev 19 | 20 | ## Extra Resources 21 | 22 | There are some great resources online to go further with the course material. 23 | 24 | - [tour of Go](https://tour.golang.org/list) 25 | - [Effective Go](https://golang.org/doc/effective_go.html) 26 | - [Go by Example](https://gobyexample.com/) 27 | - [Go docs](https://golang.org/doc/) 28 | - [The Go Blog](https://blog.golang.org/) 29 | 30 | [slides]: https://static.frontendmasters.com/resources/2019-09-24-golang/golang.pdf 31 | [go]: https://frontendmasters.com/courses/go-for-js-devs/ 32 | [godownload]: https://golang.org/dl/ 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello Front End Masters!") 7 | } 8 | -------------------------------------------------------------------------------- /todos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 66 | 67 | 68 | {{.PageTitle}} 69 | 70 | 71 | 73 | 74 |

All Todos

75 |
76 | 77 | 78 | 79 |
80 |
81 | {{if not .PageTodos }} 82 |

No Todos

83 | {{else}} 84 | {{range .PageTodos}} 85 |
86 |

{{.Title}}

87 |

{{.Content}}

88 |
89 | {{end}} 90 | {{end}} 91 |
92 | 93 | 94 | --------------------------------------------------------------------------------