├── README.md ├── design ├── README.md └── main.go └── language ├── README.md ├── arrays ├── README.md └── main.go ├── channels ├── README.md ├── exercise1 │ ├── README.md │ └── main.go └── exercise2 │ ├── README.md │ └── main.go ├── constants ├── README.md └── main.go ├── data_race ├── README.md └── main.go ├── embedding ├── README.md └── main.go ├── error_handling ├── README.md ├── exercise1 │ ├── README.md │ └── main.go └── exercise2 │ ├── README.md │ └── main.go ├── exporting ├── README.md ├── main.go └── toy │ └── toy.go ├── functions ├── README.md └── main.go ├── goroutines ├── README.md └── main.go ├── interfaces ├── README.md └── main.go ├── maps ├── README.md └── main.go ├── methods ├── README.md └── main.go ├── pointers ├── README.md ├── exercise1 │ ├── README.md │ └── main.go └── exercise2 │ ├── README.md │ └── main.go ├── slices ├── README.md └── main.go ├── struct_types ├── README.md └── main.go └── variables ├── README.md └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # 💥 Big Points 🎶 2 | 3 | - Remember LESS is MORE. 4 | 5 | - `readability` = not hiding the cost; `simplicity` = hide complexity 6 | 7 | - If you don't UNDERSTAND the `DATA`, you DON'T UNDERSTAND the PROBLEM you're working on. 8 | 9 | - Remember: `stack` (stay on stack) vs `heap` (share on heap) 10 | 11 | - Take command of `type` & Take command of `semantics` 12 | 13 | - Mantra: REDUCE, MINIMIZE, SIMPLIFY. 14 | 15 | - Never share a string or slice `unless` you're marshaling or unmarshaling. 16 | 17 | - Small is fast = because smaller data structures can stay within the cachelines. 18 | 19 | - Memory leak in go: a reference on the heap that `isn't garbage collected` AND `not being used`. 20 | 21 | - It's never a good reason to alter a data set after you pull it! 22 | 23 | - Know your length when getting a slice of a slice. 24 | 25 | - (Big NO NO) Don't mix `value semantics` WITH `pointer semantics`; vice versa. 26 | 27 | - When in doubt USE `pointer semantics`; except with time. 28 | 29 | - Method sets dictate interface compliance. 30 | 31 | - We do not use embedding to reuse state! We use embedding to reuse behavior. (WE EMBED BECAUSE WE NEED BEHAVIOR) 32 | 33 | - Group by behavior (by things that they do vs. what they are). 34 | 35 | - There is implicit conversion in go with interface types because they are valueless and same memory model. 36 | 37 | - Always `return` the `error` interface type 38 | 39 | - Don't use `pointer semantics` when dealing with `error` handling, because the addresses are always different. 40 | 41 | - You are not allowed to both: log an `error` && pass it up. 42 | 43 | - You are not allowed to create a `go routine` unless you can determine WHEN and HOW it will be terminated 44 | 45 | - Do not think of channels as queues, but as `signals`. 46 | 47 | - Don't use a buffer larger than 1; Anything more and it's unsafe. 48 | - Using `<=1` = somewhat of a garuntee. 49 | - Easier to catch problems/bugs. 50 | -------------------------------------------------------------------------------- /design/README.md: -------------------------------------------------------------------------------- 1 | ## Interface and Composition Design 2 | 3 | Composition goes beyond the mechanics of type embedding and is more than just a paradigm. It is the key for maintaining stability in your software by having the ability to adapt to the data and transformation changes that are coming. 4 | 5 | ## Notes 6 | 7 | * This is much more than the mechanics of type embedding. 8 | * Declare types and implement workflows with composition in mind. 9 | * Understand the problem you are trying to solve first. This means understanding the data. 10 | * The goal is to reduce and minimize cascading changes across your software. 11 | * Interfaces provide the highest form of composition. 12 | * Don't group types by a common DNA but by a common behavior. 13 | * Everyone can work together when we focus when we do and not what we are. 14 | 15 | ## Quotes 16 | 17 | _"Methods are valid when it is practical or reasonable for a piece of data to expose a capability." - William Kennedy_ 18 | 19 | ## Design Guidelines 20 | 21 | **Design Philosophy:** 22 | 23 | * Interfaces give programs structure. 24 | * Interfaces encourage design by composition. 25 | * Interfaces enable and enforce clean divisions between components. 26 | * The standardization of interfaces can set clear and consistent expectations. 27 | * Decoupling means reducing the dependencies between components and the types they use. 28 | * This leads to correctness, quality and performance. 29 | * Interfaces allow you to group concrete types by what they do. 30 | * Don't group types by a common DNA but by a common behavior. 31 | * Everyone can work together when we focus on what we do and not who we are. 32 | * Interfaces help your code decouple itself from change. 33 | * You must do your best to understand what could change and use interfaces to decouple. 34 | * Interfaces with more than one method have more than one reason to change. 35 | * Uncertainty about change is not a license to guess but a directive to STOP and learn more. 36 | * You must distinguish between code that: 37 | * defends against fraud vs protects against accidents 38 | 39 | **Validation:** 40 | 41 | Declare an interface when: 42 | * users of the API need to provide an implementation detail. 43 | * API’s have multiple implementations they need to maintain internally. 44 | * parts of the API that can change have been identified and require decoupling. 45 | 46 | Don't declare an interface: 47 | * for the sake of using an interface. 48 | * to generalize an algorithm. 49 | * when users can declare their own interfaces. 50 | * if it's not clear how the ineterface makes the code better. 51 | 52 | ### Exercise 1 53 | 54 | Using the template, declare a set of concrete types that implement the set of predefined interface types. Then create values of these types and use them to complete a set of predefined tasks. 55 | 56 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/uY6KMprfMR)) | 57 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/nbd3gnLlih)) 58 | 59 | Solution: 60 | ```go 61 | package main 62 | 63 | // Add import(s). 64 | import "fmt" 65 | 66 | // administrator represents a person or other entity capable of administering 67 | // hardware and software infrastructure. 68 | type administrator interface { 69 | administrate(system string) 70 | } 71 | 72 | // developer represents a person or other entity capable of writing software. 73 | type developer interface { 74 | develop(system string) 75 | } 76 | 77 | // ============================================================================= 78 | 79 | // adminlist represents a group of administrators. 80 | type adminlist struct { 81 | list []administrator 82 | } 83 | 84 | // Enqueue adds an administrator to the adminlist. 85 | func (l *adminlist) Enqueue(a administrator) { 86 | l.list = append(l.list, a) 87 | } 88 | 89 | // Dequeue removes an administrator from the adminlist. 90 | func (l *adminlist) Dequeue() administrator { 91 | a := l.list[0] 92 | l.list = l.list[1:] 93 | return a 94 | } 95 | 96 | // ============================================================================= 97 | 98 | // devlist represents a group of developers. 99 | type devlist struct { 100 | list []developer 101 | } 102 | 103 | // Enqueue adds a developer to the devlist. 104 | func (l *devlist) Enqueue(d developer) { 105 | l.list = append(l.list, d) 106 | } 107 | 108 | // Dequeue removes a developer from the devlist. 109 | func (l *devlist) Dequeue() developer { 110 | d := l.list[0] 111 | l.list = l.list[1:] 112 | return d 113 | } 114 | 115 | // ============================================================================= 116 | 117 | // Declare a concrete type named sysadmin with a name field of type string. 118 | type sysadmin struct { 119 | name string 120 | } 121 | 122 | // Declare a method named administrate for the sysadmin type, implementing the 123 | // administrator interface. administrate should print out the name of the 124 | // sysadmin, as well as the system they are administering. 125 | func (s *sysadmin) administrate(system string) { 126 | fmt.Println(s.name, "is administering", system) 127 | } 128 | 129 | // Declare a concrete type named programmer with a name field of type string. 130 | type programmer struct { 131 | name string 132 | } 133 | 134 | // Declare a method named develop for the programmer type, implementing the 135 | // developer interface. develop should print out the name of the 136 | // programmer, as well as the system they are coding. 137 | func (p *programmer) develop(system string) { 138 | fmt.Println(p.name, "is developing", system) 139 | } 140 | 141 | // Declare a concrete type named company. Declare it as the composition of 142 | // the administrator and developer interface types. 143 | type company struct { 144 | administrator 145 | developer 146 | } 147 | 148 | // ============================================================================= 149 | 150 | func main() { 151 | 152 | // Create a variable named admins of type adminlist. 153 | var admins adminlist 154 | 155 | // Create a variable named devs of type devlist. 156 | var devs devlist 157 | 158 | // Enqueue a new sysadmin onto admins. 159 | admins.Enqueue(&sysadmin{"John"}) 160 | 161 | // Enqueue two new programmers onto devs. 162 | devs.Enqueue(&programmer{"Mary"}) 163 | devs.Enqueue(&programmer{"Steve"}) 164 | 165 | // Create a variable named cmp of type company, and initialize it by 166 | // hiring (dequeuing) an administrator from admins and a developer from devs. 167 | cmp := company{ 168 | administrator: admins.Dequeue(), 169 | developer: devs.Dequeue(), 170 | } 171 | 172 | // Enqueue the company value on both lists since the company implements 173 | // each interface. 174 | admins.Enqueue(&cmp) 175 | devs.Enqueue(&cmp) 176 | 177 | // A set of tasks for administrators and developers to perform. 178 | tasks := []struct { 179 | needsAdmin bool 180 | system string 181 | }{ 182 | {needsAdmin: false, system: "xenia"}, 183 | {needsAdmin: true, system: "pillar"}, 184 | {needsAdmin: false, system: "omega"}, 185 | } 186 | 187 | // Iterate over tasks. 188 | for _, task := range tasks { 189 | 190 | // Check if the task needs an administrator else use a developer. 191 | if task.needsAdmin { 192 | 193 | // Dequeue an administrator value from the admins list and 194 | // call the administrate method. 195 | adm := admins.Dequeue() 196 | adm.administrate(task.system) 197 | 198 | continue 199 | } 200 | 201 | // Dequeue a developer value from the devs list and 202 | // call the develop method. 203 | dev := devs.Dequeue() 204 | dev.develop(task.system) 205 | } 206 | } 207 | ``` 208 | -------------------------------------------------------------------------------- /design/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Using the template, declare a set of concrete types that implement the set 5 | // of predefined interface types. Then create values of these types and use 6 | // them to complete a set of predefined tasks. 7 | package main 8 | 9 | // Add import(s). 10 | import "fmt" 11 | 12 | // administrator represents a person or other entity capable of administering 13 | // hardware and software infrastructure. 14 | type administrator interface { 15 | administrate(system string) 16 | } 17 | 18 | // developer represents a person or other entity capable of writing software. 19 | type developer interface { 20 | develop(system string) 21 | } 22 | 23 | // ============================================================================= 24 | 25 | // adminlist represents a group of administrators. 26 | type adminlist struct { 27 | list []administrator 28 | } 29 | 30 | // Enqueue adds an administrator to the adminlist. 31 | func (l *adminlist) Enqueue(a administrator) { 32 | l.list = append(l.list, a) 33 | } 34 | 35 | // Dequeue removes an administrator from the adminlist. 36 | func (l *adminlist) Dequeue() administrator { 37 | a := l.list[0] 38 | l.list = l.list[1:] 39 | return a 40 | } 41 | 42 | // ============================================================================= 43 | 44 | // devlist represents a group of developers. 45 | type devlist struct { 46 | list []developer 47 | } 48 | 49 | // Enqueue adds a developer to the devlist. 50 | func (l *devlist) Enqueue(d developer) { 51 | l.list = append(l.list, d) 52 | } 53 | 54 | // Dequeue removes a developer from the devlist. 55 | func (l *devlist) Dequeue() developer { 56 | d := l.list[0] 57 | l.list = l.list[1:] 58 | return d 59 | } 60 | 61 | // ============================================================================= 62 | 63 | // Declare a concrete type named sysadmin with a name field of type string. 64 | type sysadmin struct { 65 | name string 66 | } 67 | 68 | // Declare a method named administrate for the sysadmin type, implementing the 69 | // administrator interface. administrate should print out the name of the 70 | // sysadmin, as well as the system they are administering. 71 | func (s *sysadmin) administrate(system string) { 72 | fmt.Printf("Sys Admin = %+v, is on system: %+v\n", s.name, system) 73 | } 74 | 75 | // Declare a concrete type named programmer with a name field of type string. 76 | type programmer struct { 77 | name string 78 | } 79 | 80 | // Declare a method named develop for the programmer type, implementing the 81 | // developer interface. develop should print out the name of the 82 | // programmer, as well as the system they are coding. 83 | func (p *programmer) develop(system string) { 84 | fmt.Printf("Programmer = %+v, is on system: %+v\n", p.name, system) 85 | } 86 | 87 | // Declare a concrete type named company. Declare it as the composition of 88 | // the administrator and developer interface types. 89 | type company struct { 90 | administrator 91 | developer 92 | } 93 | 94 | // ============================================================================= 95 | 96 | func main() { 97 | 98 | // Create a variable named admins of type adminlist. 99 | var admins adminlist 100 | 101 | // Create a variable named devs of type devlist. 102 | var devs devlist 103 | 104 | // Enqueue a new sysadmin onto admins. 105 | admins.Enqueue(&sysadmin{"Neil Anderson"}) 106 | 107 | // Enqueue two new programmers onto devs. 108 | devs.Enqueue(&programmer{"Tj Holowaychuk"}) 109 | devs.Enqueue(&programmer{"Dave Cheney"}) 110 | 111 | // Create a variable named cmp of type company, and initialize it by 112 | // hiring (dequeuing) an administrator from admins and a developer from devs. 113 | cmp := company{ 114 | administrator: admins.Dequeue(), 115 | developer: devs.Dequeue(), 116 | } 117 | 118 | // Enqueue the company value on both lists since the company implements 119 | // each interface. 120 | admins.Enqueue(&cmp) 121 | devs.Enqueue(&cmp) 122 | 123 | // A set of tasks for administrators and developers to perform. 124 | tasks := []struct { 125 | needsAdmin bool 126 | system string 127 | }{ 128 | {needsAdmin: false, system: "xenia"}, 129 | {needsAdmin: true, system: "pillar"}, 130 | {needsAdmin: false, system: "omega"}, 131 | } 132 | 133 | // Iterate over tasks. 134 | for _, task := range tasks { 135 | 136 | // Check if the task needs an administrator else use a developer. 137 | if task.needsAdmin { 138 | 139 | // Dequeue an administrator value from the admins list and 140 | // call the administrate method. 141 | admin := admins.Dequeue() 142 | admin.administrate(task.system) 143 | 144 | continue 145 | } 146 | 147 | // Dequeue a developer value from the devs list and 148 | // call the develop method. 149 | dev := devs.Dequeue() 150 | dev.develop(task.system) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /language/README.md: -------------------------------------------------------------------------------- 1 | # Ultimate Go 4/4/17 - 4/5/17 2 | 3 | ### Remember less is more 4 | - Hardware is the platform; machine is the model. 5 | 6 | - NUMA = non-uniform memory access; this is complex. 7 | 8 | - Productivity > Performance; keep moving forward 9 | 10 | - --Debugger; debugger == death; cannot maintain mental model 11 | 12 | ### readability = not hiding the cost; simplicity = hide complexity 13 | 14 | - `var` always for zero value (bill standard); `var zero int` vs `zero := 0` 15 | 16 | ### If you don't understand the data, you don't understand the problem you're working on. 17 | 18 | 19 | ## Do not create values using pointer semantics 20 | 21 | #### stack (stay on stack) vs heap (share on heap) 22 | 23 | Go allows: 24 | - Mechanical sympothy - layout data and access it. 25 | - Data transformation. 26 | - Reduce pressure on GC. 27 | 28 | - pointers are for sharing; sharing data across program boundries. 29 | - program boundry: fn call, moving data across 2 goroutines. 30 | 31 | go gives 3 blocks of memory: 32 | data segment, stack (1mb), heap 33 | 34 | - stack frames are calculated at compile time. 35 | 36 | - GC operates against the heap. 37 | - values on heap are meant to be shared. 38 | - every fn call requires new frame; every time you allocate a frame you're initializing a frame; clean before usage. 39 | 40 | - stacks go down, memory addresses go down. 41 | 42 | - Stacks are 2kb. 43 | 44 | - pointer variables = * ponter type's name; always stores memory addresses; all allocate the same amount of memory 4 || 8 bytes. 45 | - Read and write to the value that the address points to. 46 | 47 | - escape analysis (algorithm): Asks can we keep this value on the stack frame for this fn? If shared unsafely it moves to heap; get to maintain value semantics or pointer semantics. 48 | 49 | - sharing down the call stack doesn't create an allocation to the heap. 50 | - sharing a value up the call stack `return &u`; it's on the heap. 51 | - heap values once allocated do not move; unlike stack values they can grow. 52 | 53 | ```go 54 | count := 10 55 | // what's in the box, where is the box 56 | println( count, &count) 57 | ``` 58 | ## do not create a value using pointer semantics 59 | ```go 60 | // hides readability 61 | u := &user { 62 | name: "Bill", 63 | email: "bill@email.env", 64 | } 65 | ``` 66 | - You can share going down the call stack. Can't share going up. 67 | 68 | - GC's pacing (algorithm) - monitoring live heap trying to keep it at 2mb. 69 | 70 | - GC turning on is a 25% performance hit. 71 | 72 | ### Take command of type & Take command of semantics 73 | 74 | - cache hides latency costs. 75 | 76 | TLB - table lookaside buffers 77 | 78 | - arrays = predicatble access patterns. 79 | 80 | TODO: [code::dive conference 2014 - Scott Meyers: Cpu Caches and Why You Care ](https://www.youtube.com/watch?v=WDIkqP4JbkE) 81 | 82 | - HARDWARE `<3` arrays == mechanical sympathy. 83 | 84 | - OOP--, because OOP == linked lists; child parent taxonomies. 85 | 86 | ### mantra: REDUCE, MINIMIZE, SIMPLIFY 87 | 88 | 89 | ## slices 90 | - `make()` is for: arrays, slices, channels 91 | - 3 words: 92 | - 1st structure is a pointer 93 | - length = read and write 94 | - capacity = total number of elements 95 | 96 | ### Never share a string or slice UNLESS you're marshaling or unmarshaling 97 | 98 | ### Small is fast = because smaller data structures can stay within the cachelines 99 | ```go 100 | var data []string // zero value 101 | data := []string{} // empty value 102 | ``` 103 | empty struct `struct{}`; you could create billion vals with 0 allocation. 104 | 105 | ### Memory leak in go: a reference on the heap that isn't Getting GC and not being used 106 | 107 | - Deterining a 2nd arg in a slice[a:b] add b with how many you need. 108 | - After a 1k elements in an array it stops doubling and only grows by a 1/4 (25%). 109 | - Append always works on length. 110 | 111 | - a slice of pointers is essentially a linked list. 112 | - `append` is a mutation operation. 113 | 114 | ### Never a good reason to alter a data set after you pull it!!! 115 | 116 | - Pointers are best passed down vs up because it would have to go in the heap. 117 | - Value semantics mean a copy of the value is passed across program boundaries. 118 | - Pointer semantics mean a copy of the values address is passed across program boundaries. 119 | 120 | ### Know your length when getting a slice of a slice 121 | 122 | - data race; 2 goroutines, 1 performing a read 1 performing a write at the same time. 123 | 124 | ## Strings 125 | - `rune` is an alias of type int32 126 | - `byte` is an alias for uint8 127 | - A string is immutable 128 | 129 | > Every array in go is a slice waiting to happen. 130 | 131 | 132 | # Design 133 | 134 | > "Methods are valid when it is practical or reasonable for a piece of data to expose a capability." - William Kennedy 135 | 136 | - DATA vs BEHAVIOR 137 | 138 | - rule thumb: variable reciever should be shorter than 3 characters 139 | 140 | ### NO NO! Don't mix value and pointer semantics. example: 141 | ```go 142 | func changeName(u *user, name string) { 143 | u.name = name 144 | } 145 | ``` 146 | ### WHEN IN DOUBT USE POINTER SEMANTICS, except with time 147 | 148 | - Your factory functions dictates semantic consistency. 149 | 150 | - Value semantics leverage the stack and take pressure off the heap. 151 | - Pointer semantics leverage effciency and the heap for sharing. 152 | 153 | - Applying pointer semantics to real world. Can you copy a person? You should use a pointer. 154 | 155 | - Concurrency is complex; Life will be simplier without using the `go` keyword 156 | 157 | ## Interfaces 158 | 159 | - Interfaces of a contract of behavior 160 | 161 | [sqlx > gorm](http://jmoiron.github.io/sqlx/) 162 | 163 | # Day 2 164 | 165 | ## Interface vs concrete type 166 | 167 | - Basically 2 type systems (interface, concrete type) 168 | - A concrete type is you can apply a method too. 169 | - Interfaces define behavior; interface types are `value-less`. 170 | - A relationship of storage! 171 | - goes onto the heap. 172 | 173 | ### Method sets dictate interface compliance 174 | 175 | - [Method sets wiki](https://github.com/golang/go/wiki/MethodSets) 176 | 177 | rules: 178 | 1. If you have a *T you can call methods that have a receiver type of *T as well as methods that have a receiver type of T (the passage you quoted, Method Sets). 179 | 2. If you have a T and it is addressable you can call methods that have a receiver type of *T as well as methods that have a receiver type of T, because the method call t.Meth() will be equivalent to (&t).Meth() (Calls). 180 | 3. If you have a T and it isn't addressable, you can only call methods that have a receiver type of T, not *T. 181 | 4. If you have an interface I, and some or all of the methods in I's method set are provided by methods with a receiver of *T (with the remainder being provided by methods with a receiver of T), then *T satisfies the interface I, but T doesn't. That is because *T's method set includes T's, but not the other way around (back to the first point again). 182 | 183 | 184 | - literal values only exist at compile time. 185 | - Pointer semantics or value semantics matter when implementing an interface. 186 | - If using a pointer receiver on a method, only pointer values will satisfy interface (integrity) 187 | - Nouns as pointers? 188 | 189 | ### We do not use embedding to reuse state! We use embedding to reuse behavior. (WE EMBED BECAUSE WE NEED BEHAVIOR) 190 | 191 | - concrete type system: algorithm (data), effciency 192 | - interface type system: decoupling, behavior 193 | 194 | - Start at concrete types (the data), then implement behavior with interfaces. 195 | 196 | ### Group by behavior (by things that they do vs. what they are) 197 | 198 | - Archetecting/designing/APIs is all about that behavior. 199 | 200 | ## Rules of knowing being done 201 | - 80% test coverage on everything. 100% on happy path. 202 | - Decoupling from changes of today. 203 | 204 | Design example: 205 | [xenia transfer to pillar](https://play.golang.org/p/ES4BOnDX6O) 206 | 207 | - Do interfaces last 208 | 209 | Interfaces cost: 210 | - Allocation (goes onto heap) 211 | - Indirection (fn call) 212 | 213 | ### implicit conversion in go with interface types because valueless and same memory model 214 | 215 | Two ways 2 shutdown program: 216 | 1. panic 217 | 2. os.Exit / log.Fatal 218 | - if need stack trace { log.Fatal } else os.Exit 219 | 220 | ## How to use an interface: 221 | Declare an interface when: 222 | * users of the API need to provide an implementation detail. 223 | * API’s have multiple implementations they need to maintain internally. 224 | * parts of the API that can change have been identified and require decoupling. 225 | 226 | Don't declare an interface: 227 | * for the sake of using an interface. 228 | * to generalize an algorithm. 229 | * when users can declare their own interfaces. 230 | * if it's not clear how the ineterface makes the code better. 231 | 232 | ## Error handling 233 | 234 | - If your user must parse the string that comes out of this implementaion, YOU FAILED THEM 235 | 236 | - `err != nil` Is there a concrete type stored in this interface; err is not set to a zero value. 237 | 238 | - `if` statement for negative path processing/err handling. 239 | - `switch` > `else`. 240 | - `if {} else{}` for assignment only. 241 | 242 | - One `err` return per fn, this will allow for concise context. 243 | 244 | - `err.(type) // type assertion on the concrete type` = is walking away from decoupling with interfaces; 245 | 246 | 4 things to allow for custom err types exported: 247 | 1. temporary 248 | 2. timeout 249 | 3. not-found 250 | 4. not-authorized 251 | 252 | - custom error types are just artifacts. 253 | 254 | ### Always return the error interface type 255 | 256 | ### Don't use pointer semantics when dealing with error handling, because addresses are always different 257 | 258 | - Go perspective logging is expensive, there is an allocation or 2. 259 | - Logging an error anything that is not actionable is wasting resources. 260 | - Logging is apart of error handling (not decoupled) | error is not handled until it's logged. 261 | - Who get's to handle the error? 262 | - If you pass the error up the call chain ADD CONTEXT. 263 | 264 | ### You are not allowed to log an error && pass it up. 265 | 266 | - It's best to know how to handle err logging from day 1. 267 | 268 | [Best error package for handling context](https://github.com/pkg/errors) 269 | 270 | 271 | ## Concurrency 272 | 273 | A thread can be in 1 of 3 states: 274 | 1. Execution state = Running on a core. 275 | 2. Runnable state = wants to execute but sitting in a queue. 276 | 3. Sleep state = Waiting for something to be moved to runnable. 277 | 278 | - Go's runtime is a cooperative scheduler. 279 | - Mimics linux scheduler. 280 | - Sits on top of OS sheduler. 281 | - CPU can be in 1 of 2 modes: Kernal mode (unrestricted) OR user mode (restricted). 282 | 283 | Scheduling decision: 284 | 1. `go` keyword 285 | 2. garbage collection 286 | 3. system call; like `fmt.Println` 287 | 288 | - Go routine time idles so that it tricks the OS to not physically make a context switch. 289 | 290 | - Think of concurrency like parent hood. Everytime you run a go routine you're brining a child into the world. 291 | 292 | ### You are not allowed to create a go routine unless you can determine WHEN and HOW it will be terminated 293 | 294 | - Leverage the `sync.wg` package to manage go routines. 295 | - Don't use `wg.Add` within a `for` loop. 296 | 297 | 2 rules of thumbs for multi threaded software: 298 | 1. Some shared state that multiple go routines want to access at the same time 299 | - Data race = read and write same data same time. 300 | 2. Coordinate 2+ goroutines to 301 | 302 | - Bill says: `atomic` > `mutex` 303 | 304 | ### Do not think of channels as queues 305 | 306 | - channels serve one purpose `signaling`. 307 | - signals with data. 308 | - signals without data. 309 | 310 | - Two types of channels: 311 | - Un buffered. 312 | * (Rob Pike always uses this type of channel) They all you to know 100% garuntee that the signal has been received. 313 | - This reduces risk 314 | - Signaling with the data, when the other go routine recieves it the intializer releases. 315 | - Cost is latency (because it's blocking) 316 | - Buffered. 317 | 318 | ### You are not allowed to use a buffer larger than 1, because this can give a garuntee and reduce latency 319 | 320 | - `send` is a binary opertaion 321 | - `receive` is a unary operation 322 | 323 | - `ch := make(chan struct{})` unbuffered channel singaling without data 324 | 325 | - Channel is in one of 2 states: open or closed. 326 | - Once you `close` a channel you cannot re-open 327 | 328 | - `select` in goroutines for cancellation 329 | 330 | - Ask if a buffered channel is 1 > if it is a "fan out pattern". 331 | 332 | - you'll `panic` if you try to `close` a channel twice, or if you try to send on a closed channel. 333 | -------------------------------------------------------------------------------- /language/arrays/README.md: -------------------------------------------------------------------------------- 1 | ## Arrays 2 | 3 | Arrays are a special data structure in Go that allow us to allocate contiguous blocks of fixed size memory. Arrays have some special features in Go related to how they are declared and viewed as types. 4 | 5 | ## Notes 6 | 7 | * If you don't understand the data, you don't understand the problem. 8 | * If you don't understand the cost of solving the problem, you can't reason about the problem. 9 | * If you don't understand the hardware, you can't reason about the cost of solving the problem. 10 | * Arrays are fixed length data structures that can't change. 11 | * Arrays of different sizes are considered to be of different types. 12 | * Memory is allocated as a contiguous block. 13 | * Go gives you control over spacial locality. 14 | 15 | ## Design Guidelines 16 | 17 | * If you don't understand the data, you don't understand the problem. 18 | * All problems are unique and specific to the data you are working with. 19 | * Data transformations are at the heart of solving problems. Each function, method and work-flow must focus on implementing the specific data transformations required to solve the problems. 20 | * If your data is changing, your problems are changing. When your problems are changing, the data transformations needs to change with it. 21 | * Uncertainty about the data is not a license to guess but a directive to STOP and learn more. 22 | * Solving problems you don't have, creates more problems you now do. 23 | * If performance matters, you must have mechanical sympathy for how the hardware and operating system work. 24 | * Minimize, simplify and REDUCE the amount of code required to solve each problem. Do less work by not wasting effort. 25 | * Code that can be reasoned about and does not hide execution costs can be better understood, debugged and performance tuned. 26 | * Coupling data together and writing code that produces predictable access patterns to the data will be the most performant. 27 | * Changing data layouts can yield more significant performance improvements than changing just the algorithms. 28 | * Efficiency is obtained through algorithms but performance is obtained through data structures and layouts. 29 | 30 | ### Exercise 1 31 | 32 | Declare an array of 5 strings with each element initialized to its zero value. Declare a second array of 5 strings and initialize this array with literal string values. Assign the second array to the first and display the results of the first array. Display the string value and address of each element. 33 | 34 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/H1jTYxk7o6)) | 35 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/i_2oDZ1ZSg)) 36 | 37 | Solution: 38 | ```go 39 | package main 40 | 41 | import "fmt" 42 | 43 | func main() { 44 | 45 | // Declare string arrays to hold names. 46 | var names [5]string 47 | 48 | // Declare an array pre-populated with friend's names. 49 | friends := [5]string{"Joe", "Ed", "Jim", "Erick", "Bill"} 50 | 51 | // Assign the array of friends to the names array. 52 | names = friends 53 | 54 | // Display each string value and address index in names. 55 | for i, name := range names { 56 | fmt.Println(name, &names[i]) 57 | } 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /language/arrays/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare an array of 5 strings with each element initialized to its zero value. 5 | // 6 | // Declare a second array of 5 strings and initialize this array with literal string 7 | // values. Assign the second array to the first and display the results of the first array. 8 | // Display the string value and address of each element. 9 | package main 10 | 11 | // Add imports. 12 | import "fmt" 13 | 14 | func main() { 15 | 16 | // Declare an array of 5 strings set to its zero value. 17 | var firstArr [5]string 18 | 19 | // Declare an array of 5 strings and pre-populate it with names. 20 | secondArr := [5]string{"one", "two", "three", "four", "five"} 21 | 22 | // Assign the populated array to the array of zero values. 23 | firstArr = secondArr 24 | 25 | // Iterate over the first array declared. 26 | // Display the string value and address of each element. 27 | for i, v := range firstArr { 28 | fmt.Printf("Index = %+v, Value = %+v\n\n", i, v) 29 | 30 | fmt.Printf("First Array address = %+v\n", &firstArr[i]) 31 | fmt.Printf("Second Array address = %+v\n\n", &secondArr[i]) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /language/channels/README.md: -------------------------------------------------------------------------------- 1 | ## Channels 2 | 3 | Channels allow goroutines to communicate with each other through the use of signaling semantics. Channels accomplish this signaling through the use of sending/receiving data or by identifying state changes on individual channels. Don't architect software with the idea of channels being a queue, focus on signaling and the semantics that simplify the orchestration required. 4 | 5 | ### Channel Design 6 | 7 | Channels allow goroutines to communicate with each other through the use of signaling semantics. Channels accomplish this signaling through the use of sending/receiving data or by identifying state changes on individual channels. Don't architect software with the idea of channels being a queue, focus on signaling and the semantics that simplify the orchestration required. 8 | 9 | **Language Mechanics:** 10 | 11 | * Use channels to orchestrate and coordinate goroutines. 12 | * Focus on the signaling semantics and not the sharing of data. 13 | * Signal with data or without data. 14 | * Question their use for synchronizing access to shared state. 15 | * _There are cases where channels can be simpler for this but initially question._ 16 | * Unbuffered channels: 17 | * Blocks the sending goroutine from moving forward until a different goroutine has received the data signal. 18 | * The sending goroutine gains immediate knowledge their data signal has been received. 19 | * Both goroutines involved must be at the channel at the same time. 20 | * More important: The Receive happens before the Send. 21 | * Trade-offs: 22 | * We take the benefit of knowing the data signal has been received for the cost of higher blocking latency. 23 | * Buffered channels: 24 | * Does NOT block the sending goroutine from moving forward until a different goroutine has received the data signal. 25 | * The sending goroutine gains no knowledge that their data signal has been received. 26 | * Both goroutines involved don't need to be at the channel at the same time. 27 | * More important: The Send happens before the Receive. 28 | * Trade-offs: 29 | * We take the benefit of reducing blocking latency for the cost of not knowing if/when the data signal is received. 30 | * Closing channels: 31 | * Signaling without data. 32 | * Perfect for signaling cancellations and deadlines. 33 | * NIL channels: 34 | * Turn off signaling 35 | * Perfect for rate limiting or short term stoppages. 36 | 37 | **Design Philosophy:** 38 | 39 | Depending on the problem you are solving, you may require different channel semantics. Depending on the semantics you need, different architectural choices must be taken. 40 | 41 | * If any given Send on a channel `CAN` cause the goroutine to block: 42 | * You have less buffers compared to the number of goroutines that will perform a Send at any given time. 43 | * An example would be a resource pool of database connections. 44 | * This requires knowing what happens when the Send blocks because this will create a situation of back pressure inside the application in front of the channel. 45 | * The things discussed above about [writing concurrent software](https://github.com/ardanlabs/gotraining/blob/master/reading/design_guidelines.md#writing-concurrent-software) must be taken into account for this channel. 46 | * Not knowing what happens when the Send blocks on the channel is not a license to guess but a directive to STOP, understand and take appropriate measures. 47 | * If any given Send on a channel `WON'T` cause the goroutine to block: 48 | * You have a buffer for every goroutine that will perform a Send. 49 | * You will abandon the Send immediately if it can't be performed. 50 | * An example would be a fan out pattern or pipelining. 51 | * This requires knowing if the size of the buffer is appropriate and if it is acceptable to abandon the Send. 52 | * Less is more with buffers. 53 | * Don’t think about performance when thinking about buffers. 54 | * Buffers can help to reduce blocking latency between signaling. 55 | * Reducing blocking latency towards zero does not necessarily mean better throughput. 56 | * If a buffer of one is giving you good enough throughput then keep it. 57 | * Question buffers that are larger than one and measure for size. 58 | * Find the smallest buffer possible that provides good enough throughput. 59 | -------------------------------------------------------------------------------- /language/channels/exercise1/README.md: -------------------------------------------------------------------------------- 1 | ### Exercise 1 2 | Write a program where two goroutines pass an integer back and forth ten times. Display when each goroutine receives the integer. Increment the integer with each pass. Once the integer equals ten, terminate the program cleanly. 3 | 4 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/BUNf38ZLka)) | 5 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/nCYvfXQwgU)) 6 | 7 | Solution: 8 | ```go 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "sync" 14 | ) 15 | 16 | func main() { 17 | 18 | // Create an unbuffered channel. 19 | share := make(chan int) 20 | 21 | // Create the WaitGroup and add a count 22 | // of two, one for each goroutine. 23 | var wg sync.WaitGroup 24 | wg.Add(2) 25 | 26 | // Launch two goroutines. 27 | go func() { 28 | goroutine("Bill", share) 29 | wg.Done() 30 | }() 31 | 32 | go func() { 33 | goroutine("Lisa", share) 34 | wg.Done() 35 | }() 36 | 37 | // Start the sharing. 38 | share <- 1 39 | 40 | // Wait for the program to finish. 41 | wg.Wait() 42 | } 43 | 44 | // goroutine simulates sharing a value. 45 | func goroutine(name string, share chan int) { 46 | for { 47 | 48 | // Wait for the ball to be hit back to us. 49 | value, ok := <-share 50 | if !ok { 51 | 52 | // If the channel was closed, return. 53 | fmt.Printf("Goroutine %s Down\n", name) 54 | return 55 | } 56 | 57 | // Display the value. 58 | fmt.Printf("Goroutine %s Inc %d\n", name, value) 59 | 60 | // Terminate when the value is 10. 61 | if value == 10 { 62 | close(share) 63 | fmt.Printf("Goroutine %s Down\n", name) 64 | return 65 | } 66 | 67 | // Increment the value and send it 68 | // over the channel. 69 | share <- (value + 1) 70 | } 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /language/channels/exercise1/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Write a program where two goroutines pass an integer back and forth 5 | // ten times. Display when each goroutine receives the integer. Increment 6 | // the integer with each pass. Once the integer equals ten, terminate 7 | // the program cleanly. 8 | package main 9 | 10 | // Add imports. 11 | import ( 12 | "fmt" 13 | "sync" 14 | ) 15 | 16 | func main() { 17 | 18 | // Create an unbuffered channel. 19 | ch := make(chan int) 20 | 21 | // Create the WaitGroup and add a count 22 | // of two, one for each goroutine. 23 | var wg sync.WaitGroup 24 | wg.Add(2) 25 | 26 | // Launch the goroutine and handle Done. 27 | go func() { 28 | goroutine("FIRST ROUTINE", ch) 29 | wg.Done() 30 | }() 31 | 32 | // Launch the goroutine and handle Done. 33 | go func() { 34 | goroutine("SECOND ROUTINE", ch) 35 | wg.Done() 36 | }() 37 | 38 | // Send a value to start the counting. 39 | ch <- 1 40 | 41 | // Wait for the program to finish. 42 | wg.Wait() 43 | } 44 | 45 | // goroutine simulates sharing a value. 46 | func goroutine(title string, signal chan int) { 47 | for { 48 | // Wait for the value to be sent. 49 | // If the channel was closed, return. 50 | value, ok := <-signal 51 | if !ok { 52 | fmt.Printf("run %+v", title) 53 | return 54 | } 55 | 56 | // Display the value. 57 | fmt.Printf("%s signaling %d\n\n", title, value) 58 | 59 | // Terminate when the value is 10. 60 | if value == 10 { 61 | fmt.Printf("CLOSED %+v\n", title) 62 | close(signal) 63 | return 64 | } 65 | 66 | // Increment the value and send it 67 | // over the channel. 68 | signal <- (value + 1) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /language/channels/exercise2/README.md: -------------------------------------------------------------------------------- 1 | ### Exercise 2 2 | Write a program that uses a fan out pattern to generate 100 random numbers concurrently. Have each goroutine generate a single random number and return that number to the main goroutine over a buffered channel. Set the size of the buffer channel so no send every blocks. Don't allocate more buffers than you need. Have the main goroutine display each random number is receives and then terminate the program. 3 | 4 | [Template](exercises/template2/template2.go) ([Go Playground](http://play.golang.org/p/CpsDFNmazH)) | 5 | [Answer](exercises/exercise2/exercise2.go) ([Go Playground](http://play.golang.org/p/Li7hl3pOSu)) 6 | 7 | Solution: 8 | ```go 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "math/rand" 14 | "time" 15 | ) 16 | 17 | const ( 18 | goroutines = 100 19 | ) 20 | 21 | func init() { 22 | rand.Seed(time.Now().UnixNano()) 23 | } 24 | 25 | func main() { 26 | 27 | // Create the buffer channel with a buffer for 28 | // each goroutine to be created. 29 | values := make(chan int, goroutines) 30 | 31 | // Iterate and launch each goroutine. 32 | for gr := 0; gr < goroutines; gr++ { 33 | 34 | // Create an anonymous function for each goroutine that 35 | // generates a random number and sends it on the channel. 36 | go func() { 37 | values <- rand.Intn(1000) 38 | }() 39 | } 40 | 41 | // Create a variable to be used to track received messages. 42 | // Set the value to the number of goroutines created. 43 | wait := goroutines 44 | 45 | // Iterate receiving each value until they are all received. 46 | for wait > 0 { 47 | fmt.Println(wait, <-values) 48 | wait-- 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /language/channels/exercise2/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Write a program that uses a fan out pattern to generate 100 random numbers 5 | // concurrently. Have each goroutine generate a single random number and return 6 | // that number to the main goroutine over a buffered channel. Set the size of 7 | // the buffer channel so no send every blocks. Don't allocate more buffers than 8 | // you need. Have the main goroutine display each random number is receives and 9 | // then terminate the program. 10 | package main 11 | 12 | // Add imports. 13 | import ( 14 | "fmt" 15 | "math/rand" 16 | "time" 17 | ) 18 | 19 | // Declare constant for number of goroutines . 20 | const ( 21 | goroutines = 100 22 | ) 23 | 24 | func init() { 25 | // Seed the random number generator. 26 | rand.Seed(time.Now().UnixNano()) 27 | } 28 | 29 | func main() { 30 | 31 | // Create the buffer channel with a buffer for 32 | // each goroutine to be created. 33 | ch := make(chan int, goroutines) 34 | 35 | // Iterate and launch each goroutine. 36 | for g := 0; g < goroutines; g++ { 37 | 38 | // Create an anonymous function for each goroutine that 39 | // generates a random number and sends it on the channel. 40 | go func() { 41 | ch <- rand.Intn(100) 42 | }() 43 | } 44 | 45 | // Create a variable to be used to track received messages. 46 | // Set the value to the number of goroutines created. 47 | wait := goroutines 48 | 49 | // Iterate receiving each value until they are all received. 50 | for wait > 0 { 51 | fmt.Printf("Wait: %+v, value: %+v\n", wait, <-ch) 52 | wait-- 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /language/constants/README.md: -------------------------------------------------------------------------------- 1 | ## Constants 2 | 3 | Constants are a way to create a named identifier whose value can never change. They also provide an incredible amount of flexibility to the language. The way constants are implemented in Go is very unique. 4 | 5 | ## Notes 6 | 7 | * Constants are not variables. 8 | * They existing only at compilation. 9 | * Untyped constants can be implictly converted where typed constants and variables can't. 10 | * Think of untyped constants as having a Kind, not a Type. 11 | * Learn about explicit and implicit conversions. 12 | * See the power of constants and their use in the standard library. 13 | 14 | ### Exercise 1 15 | 16 | **Part A:** Declare an untyped and typed constant and display their values. 17 | 18 | **Part B:** Multiply two literal constants into a typed variable and display the value. 19 | 20 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/QMrOCsHjcC)) | 21 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/aUZ-7VPb9H)) 22 | 23 | Solution: 24 | ```go 25 | package main 26 | 27 | import "fmt" 28 | 29 | const ( 30 | // server is the IP address for connecting. 31 | server = "124.53.24.123" 32 | 33 | // port is the port to make that connection. 34 | port int16 = 9000 35 | ) 36 | 37 | func main() { 38 | 39 | // Display the server information. 40 | fmt.Println(server) 41 | fmt.Println(port) 42 | 43 | // Calculate the number of minutes in 5320 seconds. 44 | minutes := 5320 / 60.0 45 | fmt.Println(minutes) 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /language/constants/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare an untyped and typed constant and display their values. 5 | // 6 | // Multiply two literal constants into a typed variable and display the value. 7 | package main 8 | 9 | // Add imports. 10 | import "fmt" 11 | 12 | const ( 13 | // Declare a constant named server of kind string and assign a value. 14 | server string = "127.0.0.1" 15 | 16 | // Declare a constant named port of type integer and assign a value. 17 | port int = 8080 18 | ) 19 | 20 | func main() { 21 | 22 | // Display the value of both server and port. 23 | fmt.Printf("server = %+v:%+v\n", server, port) 24 | 25 | // Divide a constant of kind integer and kind floating point and 26 | // assign the result to a variable. 27 | TTR := 999999.99 / 10 28 | 29 | // Display the value of the variable. 30 | fmt.Printf("Time to run = %+v", TTR) 31 | } 32 | -------------------------------------------------------------------------------- /language/data_race/README.md: -------------------------------------------------------------------------------- 1 | ## Data Races 2 | 3 | A data race is when two or more goroutines attempt to read and write to the same resource at the same time. Race conditions can create bugs that totally appear random or can be never surface as they corrupt data. Atomic functions and mutexes are a way to synchronize the access of shared resources between goroutines. 4 | 5 | ## Notes 6 | 7 | * Goroutines need to be coordinated and synchronized. 8 | * When two or more goroutines attempt to access the same resource, we have a data race. 9 | * Atomic functions and mutexes can provide the support we need. 10 | 11 | ## Cache Coherency and False Sharing 12 | This content is provided by Scott Meyers from his talk in 2014 at Dive: 13 | 14 | [CPU Caches and Why You Care (30:09-38:30)](https://youtu.be/WDIkqP4JbkE?t=1809) 15 | 16 | ## Cache Coherency and False Sharing Notes 17 | 18 | * Thread memory access matters. 19 | * If your algorithm is not scaling look for false sharing problems. 20 | 21 | ## Links 22 | 23 | [Eliminate False Sharing](http://www.drdobbs.com/parallel/eliminate-false-sharing/217500206) 24 | 25 | ## False Sharing 26 | 27 | Understanding how the hardware works is an critical component to understanding how to write the most performant code you can. Knowing the basics of processor caching can help you make better decisions within the scope of writing idiomatic code. 28 | -------------------------------------------------------------------------------- /language/data_race/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Fix the race condition in this program. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "math/rand" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // numbers maintains a set of random numbers. 15 | var numbers []int 16 | 17 | // init is called prior to main. 18 | func init() { 19 | rand.Seed(time.Now().UnixNano()) 20 | } 21 | 22 | func main() { 23 | 24 | // Number of goroutines to use. 25 | const grs = 3 26 | 27 | // wg is used to manage concurrency. 28 | var wg sync.WaitGroup 29 | wg.Add(grs) 30 | 31 | // Create three goroutines to generate random numbers. 32 | for i := 0; i < grs; i++ { 33 | go func() { 34 | random(10) 35 | wg.Done() 36 | }() 37 | } 38 | 39 | // Wait for all the goroutines to finish. 40 | wg.Wait() 41 | 42 | // Display the set of random numbers. 43 | for i, number := range numbers { 44 | fmt.Println(i, number) 45 | } 46 | } 47 | 48 | // random generates random numbers and stores them into a slice. 49 | func random(amount int) { 50 | 51 | // Generate as many random numbers as specified. 52 | for i := 0; i < amount; i++ { 53 | n := rand.Intn(100) 54 | numbers = append(numbers, n) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /language/embedding/README.md: -------------------------------------------------------------------------------- 1 | ## Embedding 2 | 3 | Embedding types provides the final piece of sharing and reusing state and behavior between types. Through the use of inner type promotion, an inner type's fields and methods can be directly accessed by references of the outer type. 4 | 5 | ## Notes 6 | 7 | * Embedding types allows us to share state or behavior between types. 8 | * The inner type never loses its identity. 9 | * This is not inheritance. 10 | * Through promotion, inner type fields and methods can be accessed through the outer type. 11 | * The outer type can override the inner type's behavior. 12 | 13 | ### Exercise 1 14 | 15 | Copy the code from the template. Declare a new type called hockey which embeds the sports type. Implement the matcher interface for hockey. When implementing the match method for hockey, call into the match method for the embedded sport type to check the embedded fields first. Then create two hockey values inside the slice of matchers and perform the search. 16 | 17 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/dK0FnSnnRz)) | 18 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/ZeOIYmIw-r)) 19 | 20 | Solution: 21 | ```go 22 | package main 23 | 24 | import ( 25 | "fmt" 26 | "strings" 27 | ) 28 | 29 | // finder defines the behavior required for performing matching. 30 | type finder interface { 31 | find(needle string) bool 32 | } 33 | 34 | // sport represents a sports team. 35 | type sport struct { 36 | team string 37 | city string 38 | } 39 | 40 | // find checks the value for the specified term. 41 | func (s *sport) find(needle string) bool { 42 | return strings.Contains(s.team, needle) || strings.Contains(s.city, needle) 43 | } 44 | 45 | // hockey represents specific hockey information. 46 | type hockey struct { 47 | sport 48 | country string 49 | } 50 | 51 | // find checks the value for the specified term. 52 | func (h *hockey) find(needle string) bool { 53 | return h.sport.find(needle) || strings.Contains(h.country, needle) 54 | } 55 | 56 | func main() { 57 | 58 | // Define the term to find. 59 | needle := "Miami" 60 | 61 | // Create a slice of finder values and assign values 62 | // of the concrete hockey type. 63 | haystack := []finder{ 64 | &hockey{sport{"Panthers", "Miami"}, "USA"}, 65 | &hockey{sport{"Canadiens", "Montreal"}, "Canada"}, 66 | } 67 | 68 | // Display what we are trying to find. 69 | fmt.Println("Searching For:", needle) 70 | 71 | // Range of each haystack value and check the term. 72 | for _, hs := range haystack { 73 | if hs.find(needle) { 74 | fmt.Printf("FOUND: %+v", hs) 75 | } 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /language/embedding/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Copy the code from the template. Declare a new type called hockey 5 | // which embeds the sports type. Implement the finder interface for hockey. 6 | // When implementing the find method for hockey, call into the find method 7 | // for the embedded sport type to check the embedded fields first. Then create 8 | // two hockey values inside the slice of haystacks and perform the search. 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "strings" 14 | ) 15 | 16 | // finder defines the behavior required for performing matching. 17 | type finder interface { 18 | find(needle string) bool 19 | } 20 | 21 | // sport represents a sports team. 22 | type sport struct { 23 | team string 24 | city string 25 | } 26 | 27 | // find checks the value for the specified term. 28 | func (s *sport) find(needle string) bool { 29 | return strings.Contains(s.team, needle) || strings.Contains(s.city, needle) 30 | } 31 | 32 | // Declare a struct type named hockey that represents specific 33 | // hockey information: 34 | // - Have it embed the sport type first. 35 | // - Have it include a field with the country of the team. 36 | type hockey struct { 37 | sport 38 | country string 39 | } 40 | 41 | // find checks the value for the specified term. 42 | func (h *hockey) find(needle string) bool { 43 | // Make sure you call into find method for the embedded sport type. 44 | 45 | // Implement the search for the new fields. 46 | return h.sport.find(needle) || strings.Contains(h.country, needle) 47 | } 48 | 49 | func main() { 50 | 51 | // Define the term to find. 52 | needle := "San Jose" 53 | 54 | // Create a slice of finder values and assign values 55 | // of the concrete hockey type. 56 | haystack := []finder{ 57 | &hockey{sport{"Ducks", "Oregon"}, "USA"}, 58 | &hockey{sport{"Sharks", "San Jose"}, "USA"}, 59 | } 60 | 61 | // Display what we are trying to find. 62 | fmt.Printf("The needle in the haystack is = %+v\n", needle) 63 | 64 | // Range of each finder value and check the term. 65 | for _, h := range haystack { 66 | if h.find(needle) { 67 | fmt.Printf("Hockey Team = %+v\n", h) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /language/error_handling/README.md: -------------------------------------------------------------------------------- 1 | ## Error Handling Design 2 | 3 | Error handling is critical for making your programs reliable, trustworthy and respectful to those who depend on them. A proper error value is both specific and informative. It must allow the caller to make an informed decision about the error that has occurred. There are several ways in Go to create error values. This depends on the amount of context that needs to be provided. 4 | 5 | ## Notes 6 | 7 | * Use the default error value for static and simple formatted messages. 8 | * Create and return error variables to help the caller identify specific errors. 9 | * Create custom error types when the context of the error is more complex. 10 | * Error Values in Go aren't special, they are just values like any other, and so you have the entire language at your disposal. 11 | 12 | Solution: 13 | ``` 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /language/error_handling/exercise1/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 1 2 | 3 | Create two error variables, one called ErrInvalidValue and the other called ErrAmountTooLarge. Provide the static message for each variable. Then write a function called checkAmount that accepts a float64 type value and returns an error value. Check the value for zero and if it is, return the ErrInvalidValue. Check the value for greater than $1,000 and if it is, return the ErrAmountTooLarge. Write a main function to call the checkAmount function and check the return error value. Display a proper message to the screen. 4 | 5 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/Ltxl8Hkrkl)) | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/WHmYkHwYjf)) 6 | -------------------------------------------------------------------------------- /language/error_handling/exercise1/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Create two error variables, one called ErrInvalidValue and the other 5 | // called ErrAmountTooLarge. Provide the static message for each variable. 6 | // Then write a function called checkAmount that accepts a float64 type value 7 | // and returns an error value. Check the value for zero and if it is, return 8 | // the ErrInvalidValue. Check the value for greater than $1,000 and if it is, 9 | // return the ErrAmountTooLarge. Write a main function to call the checkAmount 10 | // function and check the return error value. Display a proper message to the screen. 11 | package main 12 | 13 | // Add imports. 14 | 15 | var ( 16 | // Declare an error variable named ErrInvalidValue using the New 17 | // function from the errors package. 18 | 19 | // Declare an error variable named ErrAmountTooLarge using the New 20 | // function from the errors package. 21 | ) 22 | 23 | // Declare a function named checkAmount that accepts a value of 24 | // type float64 and returns an error interface value. 25 | func checkAmount( /* parameter */ ) /* return arg */ { 26 | 27 | // Is the parameter equal to zero. If so then return 28 | // the error variable. 29 | 30 | // Is the parameter greater than 1000. If so then return 31 | // the other error variable. 32 | 33 | // Return nil for the error value. 34 | } 35 | 36 | func main() { 37 | 38 | // Call the checkAmount function and check the error. Then 39 | // use a switch/case to compare the error with each variable. 40 | // Add a default case. Return if there is an error. 41 | 42 | // Display everything is good. 43 | } 44 | -------------------------------------------------------------------------------- /language/error_handling/exercise2/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 2 2 | Create a custom error type called appError that contains three fields, err error, message string and code int. Implement the error interface providing your own message using these three fields. Implement a second method named temporary that returns false when the value of the code field is 9. Write a function called checkFlag that accepts a bool value. If the value is false, return a pointer of your custom error type initialized as you like. If the value is true, return a default error. Write a main function to call the checkFlag function and check the error using the temporary interface. 3 | 4 | [Template](exercises/template2/template2.go) ([Go Playground](http://play.golang.org/p/9nEdNSMa_j)) | 5 | [Answer](exercises/exercise2/exercise2.go) ([Go Playground](http://play.golang.org/p/7iX9wZX6WP)) 6 | 7 | Solution: 8 | ``` 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /language/error_handling/exercise2/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Create a custom error type called appError that contains three fields, err error, 5 | // message string and code int. Implement the error interface providing your own message 6 | // using these three fields. Implement a second method named temporary that returns false 7 | // when the value of the code field is 9. Write a function called checkFlag that accepts 8 | // a bool value. If the value is false, return a pointer of your custom error type 9 | // initialized as you like. If the value is true, return a default error. Write a main 10 | // function to call the checkFlag function and check the error using the temporary 11 | // interface. 12 | package main 13 | 14 | // Add imports. 15 | 16 | // Declare a struct type named appError with three fields, err of type error, 17 | // message of type string and code of type int. 18 | 19 | // Declare a method for the appError struct type that implements the 20 | // error interface. 21 | 22 | // Declare a method for the appError struct type named Temporary that returns 23 | // true when the value of the code field is not 9. 24 | 25 | // Declare the temporary interface type with a method named Temporary that 26 | // takes no parameters and returns a bool. 27 | 28 | // Declare a function named checkFlag that accepts a boolean value and 29 | // returns an error interface value. 30 | func checkFlag( /* parameter */ ) /* return arg */ { 31 | 32 | // If the parameter is false return an appError. 33 | 34 | // Return a default error. 35 | } 36 | 37 | func main() { 38 | 39 | // Call the checkFlag function to simulate an error of the 40 | // concrete type. 41 | 42 | // Check the concrete type and handle appropriately. 43 | switch e := err.(type) { 44 | 45 | // Apply the case for the existence of the Temporary behavior. 46 | // Log the error and write a second message only if the 47 | // error is not temporary. 48 | 49 | // Apply the default case and just log the error. 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /language/exporting/README.md: -------------------------------------------------------------------------------- 1 | ## Exporting 2 | 3 | Packages contain the basic unit of compiled code. They define a scope for the identifiers that are declared within them. Exporting is not the same as public and private semantics in other languages. But exporting is how we provide encapsulation in Go. 4 | 5 | ## Notes 6 | 7 | * Code in go is complied into packages and then linked together. 8 | * Identifiers are exported (or remain unexported) based on letter-case. 9 | * We import packages to access exported identifiers. 10 | * Any package can use a value of an unexported type, but this is annoying to use. 11 | 12 | ### Exercise 1 13 | **Part A** Create a package named toy with a single exported struct type named Toy. Add the exported fields Name and Weight. Then add two unexported fields named onHand and sold. Declare a factory function called New to create values of type toy and accept parameters for the exported fields. Then declare methods that return and update values for the unexported fields. 14 | 15 | **Part B** Create a program that imports the toy package. Use the New function to create a value of type toy. Then use the methods to set the counts and display the field values of that toy value. 16 | 17 | [Template](exercises/template1) | 18 | [Answer](exercises/exercise1) 19 | 20 | Solution: 21 | ```go 22 | // exporting/toy/toy.go 23 | package toy 24 | 25 | // Toy represents a toy we sell. 26 | type Toy struct { 27 | Name string 28 | Weight int 29 | 30 | onHand int 31 | sold int 32 | } 33 | 34 | // New creates values of type toy. 35 | func New(name string, weight int) *Toy { 36 | return &Toy{ 37 | Name: name, 38 | Weight: weight, 39 | } 40 | } 41 | 42 | // OnHand returns the current number of this 43 | // toy on hand. 44 | func (t *Toy) OnHand() int { 45 | return t.onHand 46 | } 47 | 48 | // UpdateOnHand updates the on hand count and 49 | // returns the current value. 50 | func (t *Toy) UpdateOnHand(count int) int { 51 | t.onHand += count 52 | return t.onHand 53 | } 54 | 55 | // Sold returns the current number of this 56 | // toy sold. 57 | func (t *Toy) Sold() int { 58 | return t.sold 59 | } 60 | 61 | // UpdateSold updates the sold count and 62 | // returns the current value. 63 | func (t *Toy) UpdateSold(count int) int { 64 | t.sold += count 65 | return t.sold 66 | } 67 | ``` 68 | ```go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | 74 | "github.com/ardanlabs/gotraining/topics/go/language/exporting/exercises/exercise1/toy" 75 | ) 76 | 77 | func main() { 78 | 79 | // Create a value of type toy. 80 | t := toy.New("Bat", 28) 81 | 82 | // Update the counts. 83 | t.UpdateOnHand(100) 84 | t.UpdateSold(2) 85 | 86 | // Display each field separately. 87 | fmt.Println("Name", t.Name) 88 | fmt.Println("Weight", t.Weight) 89 | fmt.Println("OnHand", t.OnHand()) 90 | fmt.Println("Sold", t.Sold()) 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /language/exporting/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Create a package named toy with a single exported struct type named Toy. Add 5 | // the exported fields Name and Weight. Then add two unexported fields named 6 | // onHand and sold. Declare a factory function called New to create values of 7 | // type toy and accept parameters for the exported fields. Then declare methods 8 | // that return and update values for the unexported fields. 9 | // 10 | // Create a program that imports the toy package. Use the New function to create a 11 | // value of type toy. Then use the methods to set the counts and display the 12 | // field values of that toy value. 13 | package main 14 | 15 | // Add imports. 16 | import ( 17 | "fmt" 18 | "github.com/derekahn/ultimate-go/exercises/exporting/toy" 19 | ) 20 | 21 | func main() { 22 | 23 | // Use the New function from the toy package to create a value of 24 | // type toy. 25 | t := toy.New("Nerf", 2) 26 | 27 | // Use the methods from the toy value to set some initialize 28 | // values. 29 | t.UpdateSold() 30 | t.UpdateOnHand() 31 | 32 | // Display each field separately from the toy value. 33 | fmt.Printf("BEFORE toy = %+v\n", t) 34 | 35 | t.UpdateSold() 36 | t.UpdateOnHand() 37 | fmt.Printf("AFTER toy = %+v\n", t) 38 | } 39 | -------------------------------------------------------------------------------- /language/exporting/toy/toy.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Package toy contains support for managing toy inventory. 5 | package toy 6 | 7 | // Declare a struct type named Toy with four fields. Name string, 8 | // Weight int, onHand int and sold int. 9 | type Toy struct { 10 | Name string 11 | Weight int 12 | 13 | onHand int 14 | sold int 15 | } 16 | 17 | // Declare a function named New that accepts values for the 18 | // exported fields. Return a pointer of type Toy that is initialized 19 | // with the parameters. 20 | func New(name string, weight int) *Toy { 21 | return &Toy{ 22 | Name: name, 23 | Weight: weight, 24 | } 25 | } 26 | 27 | // Declare a method named OnHand with a pointer receiver that 28 | // returns the current on hand count. 29 | func (t *Toy) OnHand() int { 30 | return t.onHand 31 | } 32 | 33 | // Declare a method named UpdateOnHand with a pointer receiver that 34 | // updates and returns the current on hand count. 35 | func (t *Toy) UpdateOnHand() { 36 | t.onHand++ 37 | } 38 | 39 | // Declare a method named Sold with a pointer receiver that 40 | // returns the current sold count. 41 | func (t *Toy) Sold() int { 42 | return t.sold 43 | } 44 | 45 | // Declare a method named UpdateSold with a pointer receiver that 46 | // updates and returns the current sold count. 47 | func (t *Toy) UpdateSold() { 48 | t.sold++ 49 | } 50 | -------------------------------------------------------------------------------- /language/functions/README.md: -------------------------------------------------------------------------------- 1 | ## Functions 2 | 3 | Functions are at the core of the language. They provide a mechanism to group and organize our code to separate and distinct pieces of functionality. They can be used to provide an API to the packages we write and are a core component to concurrency. 4 | 5 | ## Notes 6 | 7 | * Functions can return multiple values and most return an error value. 8 | * The error value should always be checked as part of the programming logic. 9 | * The blank identifier can be used to ignore return values. 10 | 11 | ### Exercise 1 12 | 13 | **Part A** Declare a struct type to maintain information about a user. Declare a function that creates value of and returns pointers of this type and an error value. Call this function from main and display the value. 14 | 15 | **Part B** Make a second call to your function but this time ignore the value and just test the error value. 16 | 17 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/i5wI736jpN)) | 18 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/fabhfnqJ0C)) 19 | 20 | Solution: 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | // user represents a user in the system. 27 | type user struct { 28 | name string 29 | email string 30 | } 31 | 32 | // newUser creates and returns pointers of user type values. 33 | func newUser() (*user, error) { 34 | return &user{"Bill", "bill@ardanlabs.com"}, nil 35 | } 36 | 37 | func main() { 38 | 39 | // Create a value of type user. 40 | u, err := newUser() 41 | if err != nil { 42 | fmt.Println(err) 43 | return 44 | } 45 | 46 | // Display the value. 47 | fmt.Println(*u) 48 | 49 | // Call the function and just check the error on the return. 50 | _, err = newUser() 51 | if err != nil { 52 | fmt.Println(err) 53 | return 54 | } 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /language/functions/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare a struct type to maintain information about a user. Declare a function 5 | // that creates value of and returns pointers of this type and an error value. Call 6 | // this function from main and display the value. 7 | // 8 | // Make a second call to your function but this time ignore the value and just test 9 | // the error value. 10 | package main 11 | 12 | // Add imports. 13 | import "fmt" 14 | 15 | // Declare a type named user. 16 | type user struct { 17 | name string 18 | age int 19 | } 20 | 21 | // Declare a function that creates user type values and returns a pointer 22 | // to that value and an error value of nil. 23 | func newUser(name string, age int) (u *user, err error) { 24 | if age < 21 { 25 | err = fmt.Errorf("User is not old enough... %+v < 21\n", age) 26 | return 27 | } 28 | return &user{name, age}, nil 29 | } 30 | 31 | func main() { 32 | // Use the function to create a value of type user. Check 33 | // the error being returned. 34 | u, err := newUser("jack", 24) 35 | if err != nil { 36 | fmt.Println(err) 37 | return 38 | } 39 | 40 | // Display the value that the pointer points to. 41 | fmt.Printf("user = %+v\n", u) 42 | 43 | // Call the function again and just check the error. 44 | u, err := newUser("jill", 18) 45 | if err != nil { 46 | fmt.Println(err.Error()) 47 | return 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /language/goroutines/README.md: -------------------------------------------------------------------------------- 1 | ## Goroutines 2 | 3 | Goroutines are functions that are created and scheduled to be run independently by the Go scheduler. The Go scheduler is responsible for the management and execution of goroutines. 4 | 5 | ## Notes 6 | 7 | * Goroutines are functions that are scheduled to run independently. 8 | * We must always maintain an account of running goroutines and shutdown cleanly. 9 | * Concurrency is not parallelism. 10 | * Concurrency is about dealing with lots of things at once. 11 | * Parallelism is about doing lots of things at once. 12 | 13 | ## Design Guidelines 14 | 15 | ### Concurrent Software Design 16 | 17 | Concurrency is about managing multiple things at once. Like one person washing the dishes while they are also cooking dinner. You're making progress on both but you're only ever doing one of those things at the same time. Parallelism is about doing multiple things at once. Like one person cooking and placing dirty dishes in the sink, while another washes the dishes. They are happening at the same time. 18 | 19 | Both you and the runtime have a responsibility in managing the concurrency of the application. You are responsible for managing these three things when writing concurrent software: 20 | 21 | **Design Philosophy:** 22 | 23 | * The application must startup and shutdown with integrity. 24 | * Know how and when every goroutine you create terminates. 25 | * All goroutines you create should terminate before main returns. 26 | * Applications should be capable of shutting down on demand, even under load, in a controlled way. 27 | * You want to stop accepting new requests and finish the requests you have (load shedding). 28 | * Identify and monitor critical points of back pressure that can exist inside your application. 29 | * Channels, mutexes and atomic functions can create back pressure when goroutines are required to wait. 30 | * A little back pressure is good, it means there is a good balance of concerns. 31 | * A lot of back pressure is bad, it means things are imbalanced. 32 | * Back pressure that is imbalanced will cause: 33 | * Failures inside the software and across the entire platform. 34 | * Your application to collapse, implode or freeze. 35 | * Measuring back pressure is a way to measure the health of the application. 36 | * Rate limit to prevent overwhelming back pressure inside your application. 37 | * Every system has a breaking point, you must know what it is for your application. 38 | * Applications should reject new requests as early as possible once they are overloaded. 39 | * Don’t take in more work than you can reasonably work on at a time. 40 | * Push back when you are at critical mass. Create your own external back pressure. 41 | * Use an external system for rate limiting when it is reasonable and practical. 42 | * Use timeouts to release the back pressure inside your application. 43 | * No request or task is allowed to take forever. 44 | * Identify how long users are willing to wait. 45 | * Higher-level calls should tell lower-level calls how long they have to run. 46 | * At the top level, the user should decide how long they are willing to wait. 47 | * Use the `Context` package. 48 | * Functions that users wait for should take a `Context`. 49 | * These functions should select on `<-ctx.Done()` when they would otherwise block indefinitely. 50 | * Set a timeout on a `Context` only when you have good reason to expect that a function's execution has a real time limit. 51 | * Allow the upstream caller to decide when the `Context` should be canceled. 52 | * Cancel a `Context` whenever the user abandons or explicitly aborts a call. 53 | * Architect applications to: 54 | * Identify problems when they are happening. 55 | * Stop the bleeding. 56 | * Return the system back to a normal state. 57 | 58 | ### Exercise 1 59 | 60 | **Part A** Create a program that declares two anonymous functions. One that counts down from 100 to 0 and one that counts up from 0 to 100. Display each number with an unique identifier for each goroutine. Then create goroutines from these functions and don't let main return until the goroutines complete. 61 | 62 | **Part B** Run the program in parallel. 63 | 64 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/kjtlMXkAAv)) | 65 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/pUV-FPd3CE)) 66 | 67 | Solution: 68 | ```go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | "runtime" 74 | "sync" 75 | ) 76 | 77 | func init() { 78 | 79 | // Allocate one logical processor for the scheduler to use. 80 | runtime.GOMAXPROCS(1) 81 | } 82 | 83 | func main() { 84 | 85 | // Declare a wait group and set the count to two. 86 | var wg sync.WaitGroup 87 | wg.Add(2) 88 | 89 | fmt.Println("Start Goroutines") 90 | 91 | // Declare an anonymous function and create a goroutine. 92 | go func() { 93 | // Count down from 100 to 0. 94 | for count := 100; count >= 0; count-- { 95 | fmt.Printf("[A:%d]\n", count) 96 | } 97 | 98 | // Tell main we are done. 99 | wg.Done() 100 | }() 101 | 102 | // Declare an anonymous function and create a goroutine. 103 | go func() { 104 | // Count up from 0 to 100. 105 | for count := 0; count <= 100; count++ { 106 | fmt.Printf("[B:%d]\n", count) 107 | } 108 | 109 | // Tell main we are done. 110 | wg.Done() 111 | }() 112 | 113 | // Wait for the goroutines to finish. 114 | fmt.Println("Waiting To Finish") 115 | wg.Wait() 116 | 117 | // Display "Terminating Program". 118 | fmt.Println("\nTerminating Program") 119 | } 120 | ``` 121 | -------------------------------------------------------------------------------- /language/goroutines/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Create a program that declares two anonymous functions. One that counts down from 5 | // 100 to 0 and one that counts up from 0 to 100. Display each number with an 6 | // unique identifier for each goroutine. Then create goroutines from these functions 7 | // and don't let main return until the goroutines complete. 8 | // 9 | // Run the program in parallel. 10 | package main 11 | 12 | // Add imports. 13 | import ( 14 | "fmt" 15 | "runtime" 16 | "sync" 17 | ) 18 | 19 | func init() { 20 | // Allocate one logical processor for the scheduler to use. 21 | runtime.GOMAXPROCS(1) 22 | } 23 | 24 | func main() { 25 | 26 | // Declare a wait group and set the count to two. 27 | var wg sync.WaitGroup 28 | wg.Add(2) 29 | 30 | // Declare an anonymous function and create a goroutine. 31 | go func() { 32 | 33 | // Declare a loop that counts down from 100 to 0 and 34 | // display each value. 35 | for i := 0; i < 100; i++ { 36 | fmt.Printf("i = %+v\n", i) 37 | } 38 | 39 | // Tell main we are done. 40 | wg.Done() 41 | }() 42 | 43 | go func() { 44 | 45 | // Declare a loop that counts up from 0 to 100 and 46 | // display each value. 47 | for j := 100; j > 0; j-- { 48 | fmt.Printf("j = %+v\n", j) 49 | if j == 1 { 50 | fmt.Println("==================") 51 | } 52 | } 53 | 54 | // Tell main we are done. 55 | wg.Done() 56 | }() 57 | 58 | // Wait for the goroutines to finish. 59 | wg.Wait() 60 | 61 | // Display "Terminating Program". 62 | fmt.Println("Terminating program!") 63 | } 64 | -------------------------------------------------------------------------------- /language/interfaces/README.md: -------------------------------------------------------------------------------- 1 | ## Functions 2 | 3 | Functions are at the core of the language. They provide a mechanism to group and organize our code to separate and distinct pieces of functionality. They can be used to provide an API to the packages we write and are a core component to concurrency. 4 | 5 | ## Notes 6 | 7 | * Functions can return multiple values and most return an error value. 8 | * The error value should always be checked as part of the programming logic. 9 | * The blank identifier can be used to ignore return values. 10 | 11 | ### Exercise 1 12 | 13 | **Part A** Declare an interface named speaker with a method named speak. Declare a struct named english that represents a person who speaks english and declare a struct named chinese for someone who speaks chinese. Implement the speaker interface for each struct using a value receiver and these literal strings "Hello World" and "你好世界". Declare a variable of type speaker and assign the address of a value of type english and call the method. Do it again for a value of type chinese. 14 | 15 | **Part B** Add a new function named sayHello that accepts a value of type speaker. Implement that function to call the speak method on the interface value. Then create new values of each type and use the function. 16 | 17 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/adkJ3OvYpr)) | 18 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/06fecJbfE4)) 19 | 20 | Solution: 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | // speaker implements the voice of anyone. 27 | type speaker interface { 28 | speak() 29 | } 30 | 31 | // english represents an english speaking person. 32 | type english struct{} 33 | 34 | // speak implements the speaker interface using a 35 | // value receiver. 36 | func (english) speak() { 37 | fmt.Println("Hello World") 38 | } 39 | 40 | // chinese represents a chinese speaking person. 41 | type chinese struct{} 42 | 43 | // speak implements the speaker interface using a 44 | // pointer receiver. 45 | func (*chinese) speak() { 46 | fmt.Println("你好世界") 47 | } 48 | 49 | func main() { 50 | 51 | // Declare a variable of the interface speaker type 52 | // set to its zero value. 53 | var sp speaker 54 | 55 | // Declare a variable of type english. 56 | var e english 57 | 58 | // Assign the english value to the speaker variable. 59 | sp = e 60 | 61 | // Call the speak method against the speaker variable. 62 | sp.speak() 63 | 64 | // Declare a variable of type chinese. 65 | var c chinese 66 | 67 | // Assign the chinese pointer to the speaker variable. 68 | sp = &c 69 | 70 | // Call the speak method against the speaker variable. 71 | sp.speak() 72 | 73 | // Call the sayHello function with new values and pointers 74 | // of english and chinese. 75 | sayHello(english{}) 76 | sayHello(&english{}) 77 | sayHello(&chinese{}) 78 | 79 | // Why does this not work? 80 | // sayHello(chinese{}) 81 | } 82 | 83 | // sayHello abstracts speaking functionality. 84 | func sayHello(sp speaker) { 85 | sp.speak() 86 | } 87 | ``` 88 | -------------------------------------------------------------------------------- /language/interfaces/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare an interface named speaker with a method named speak. Declare a struct 5 | // named english that represents a person who speaks english and declare a struct named 6 | // chinese for someone who speaks chinese. Implement the speaker interface for each 7 | // struct using a value receiver and these literal strings "Hello World" and "你好世界". 8 | // Declare a variable of type speaker and assign the address of a value of type english 9 | // and call the method. Do it again for a value of type chinese. 10 | // 11 | // Add a new function named sayHello that accepts a value of type speaker. 12 | // Implement that function to call the speak method on the interface value. Then create 13 | // new values of each type and use the function. 14 | package main 15 | 16 | // Add imports. 17 | import "fmt" 18 | 19 | // Declare the speaker interface with a single method called speak. 20 | type speaker interface { 21 | speak() 22 | } 23 | 24 | // Declare an empty struct type named english. 25 | type english struct{} 26 | 27 | // Declare a method named speak for the english type 28 | // using a value receiver. "Hello World" 29 | func (e english) speak() { 30 | fmt.Println("Hello World!") 31 | } 32 | 33 | // Declare an empty struct type named chinese. 34 | type chinese struct{} 35 | 36 | // Declare a method named speak for the chinese type 37 | // using a pointer receiver. "你好世界" 38 | func (s *chinese) speak() { 39 | fmt.Println("你好世界") 40 | } 41 | 42 | // sayHello accepts values of the speaker type. 43 | func sayHello(s speaker) { 44 | // Call the speak method from the speaker parameter. 45 | s.speak() 46 | } 47 | 48 | func main() { 49 | 50 | // Declare a variable of the interface speaker type 51 | // set to its zero value. 52 | var s speaker 53 | 54 | // Declare a variable of type english. 55 | e := english{} 56 | 57 | // Assign the english value to the speaker variable. 58 | s = e 59 | 60 | // Call the speak method against the speaker variable. 61 | s.speak() 62 | 63 | fmt.Println("\n=======\n") 64 | 65 | // Declare a variable of type chinese. 66 | c := new(chinese) 67 | 68 | // Assign the chinese pointer to the speaker variable. 69 | s = c 70 | 71 | // Call the speak method against the speaker variable. 72 | s.speak() 73 | 74 | // Call the sayHello function with new values and pointers 75 | // of english and chinese. 76 | fmt.Println("\n=======\n") 77 | sayHello(e) 78 | sayHello(&e) 79 | sayHello(c) 80 | } 81 | -------------------------------------------------------------------------------- /language/maps/README.md: -------------------------------------------------------------------------------- 1 | ## Maps 2 | 3 | Maps provide a data structure that allow for the storage and management of key/value pair data. 4 | 5 | ## Notes 6 | 7 | * Maps provide a way to store and retrieve key/value pairs. 8 | * The map key must be a value that can be used in an assignment statement. 9 | * Iterating over a map is always random. 10 | 11 | ### Exercise 1 12 | 13 | Declare and make a map of integer values with a string as the key. Populate the map with five values and iterate over the map to display the key/value pairs. 14 | 15 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/E2VFcOY1o6)) | 16 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/uT_pwbOgNc)) 17 | 18 | Solution: 19 | ```go 20 | // All material is licensed under the Apache License Version 2.0, January 2004 21 | // http://www.apache.org/licenses/LICENSE-2.0 22 | 23 | // Declare and make a map of integer values with a string as the key. Populate the 24 | // map with five values and iterate over the map to display the key/value pairs. 25 | package main 26 | 27 | import "fmt" 28 | 29 | func main() { 30 | 31 | // Declare and make a map of integer type values. 32 | departments := make(map[string]int) 33 | 34 | // Initialize some data into the map. 35 | departments["IT"] = 20 36 | departments["Marketing"] = 15 37 | departments["Executives"] = 5 38 | departments["Sales"] = 50 39 | departments["Security"] = 8 40 | 41 | // Display each key/value pair. 42 | for key, value := range departments { 43 | fmt.Printf("Dept: %s People: %d\n", key, value) 44 | } 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /language/maps/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare and make a map of integer values with a string as the key. Populate the 5 | // map with five values and iterate over the map to display the key/value pairs. 6 | package main 7 | 8 | // Add imports. 9 | import "fmt" 10 | 11 | func main() { 12 | 13 | // Declare and make a map of integer type values. 14 | prices := make(map[string]float32) 15 | 16 | // Initialize some data into the map. 17 | prices["shoes"] = 99.99 18 | prices["gloves"] = 68.98 19 | prices["sunglasses"] = 49.99 20 | 21 | // Display each key/value pair. 22 | for k, v := range prices { 23 | fmt.Printf("key: %+v, val: $%+v, addr: %+v\n\n", k, v, &v) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /language/methods/README.md: -------------------------------------------------------------------------------- 1 | ## Methods 2 | 3 | Methods are functions that are declared with a receiver which binds the method to a type. Methods can be used to operate on values or pointers of that type. 4 | 5 | ## Notes 6 | 7 | * Methods are functions that declare a receiver variable. 8 | * Receivers bind a method to a type and can use value or pointer semantics. 9 | * Value semantics mean a copy of the value is passed across program boundaries. 10 | * Pointer semantics mean a copy of the values address is passed across program boundaries. 11 | * Stick to a single semantic for a given type and be consistent. 12 | 13 | ## Quotes 14 | 15 | _"Methods are valid when it is practical or reasonable for a piece of data to expose a capability." - William Kennedy_ 16 | 17 | ### Exercise 1 18 | 19 | Declare a struct that represents a baseball player. Include name, atBats and hits. Declare a method that calculates a players batting average. The formula is Hits / AtBats. Declare a slice of this type and initialize the slice with several players. Iterate over the slice displaying the players name and batting average. 20 | 21 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/IG5uqVRTrc)) | 22 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/1vr9fCLEO8)) 23 | 24 | Solution: 25 | ```go 26 | package main 27 | 28 | import "fmt" 29 | 30 | // player represents a person in the game. 31 | type player struct { 32 | name string 33 | atBats int 34 | hits int 35 | } 36 | 37 | // average calculates the batting average for a player. 38 | func (p *player) average() float64 { 39 | if p.atBats == 0 { 40 | return 0.0 41 | } 42 | 43 | return float64(p.hits) / float64(p.atBats) 44 | } 45 | 46 | func main() { 47 | 48 | // Create a few players. 49 | ps := []player{ 50 | {"bill", 10, 7}, 51 | {"jim", 12, 6}, 52 | {"ed", 6, 4}, 53 | } 54 | 55 | // Display the batting average for each player. 56 | for i := range ps { 57 | // Answer: This is a value/pointer semantic 58 | fmt.Printf("%s: AVG[.%.f]\n", ps[i].name, ps[i].average()*1000) 59 | } 60 | 61 | // Why did I not choose this form? 62 | for _, p := range ps { 63 | // Answer: This is a copy semantic 64 | fmt.Printf("%s: AVG[.%.f]\n", p.name, p.average()*1000) 65 | } 66 | } 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /language/methods/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare a struct that represents a baseball player. Include name, atBats and hits. 5 | // Declare a method that calculates a players batting average. The formula is Hits / AtBats. 6 | // Declare a slice of this type and initialize the slice with several players. Iterate over 7 | // the slice displaying the players name and batting average. 8 | // package main 9 | 10 | // // Add imports. 11 | // import "fmt" 12 | 13 | // // Declare a struct that represents a ball player. 14 | // // Include field called name, atBats and hits. 15 | // type player struct { 16 | // name string 17 | // atBats int 18 | // hits int 19 | // } 20 | 21 | // // Declare a method that calculates the batting average for a player. 22 | // func (p player) average() float32 { 23 | // return float32(p.hits) / float32(p.atBats) 24 | // } 25 | 26 | // func main() { 27 | // // Create a slice of players and populate each player 28 | // // with field values. 29 | // players := []player{ 30 | // {"Jack", 9, 3}, 31 | // {"Jill", 6, 2}, 32 | // {"Bob", 12, 4}, 33 | // } 34 | 35 | // // Display the batting average for each player in the slice. 36 | // for _, p := range players { 37 | // avg := p.average() 38 | // fmt.Printf("Name = %+v, average = %+v \n", p.name, avg) 39 | // } 40 | // } 41 | 42 | package main 43 | 44 | import "fmt" 45 | 46 | // player represents a person in the game. 47 | type player struct { 48 | name string 49 | atBats int 50 | hits int 51 | } 52 | 53 | // average calculates the batting average for a player. 54 | func (p *player) average() float64 { 55 | if p.atBats == 0 { 56 | return 0.0 57 | } 58 | 59 | return float64(p.hits) / float64(p.atBats) 60 | } 61 | 62 | func main() { 63 | 64 | // Create a few players. 65 | ps := []player{ 66 | {"bill", 10, 7}, 67 | {"jim", 12, 6}, 68 | {"ed", 6, 4}, 69 | } 70 | 71 | // Display the batting average for each player. 72 | for i := range ps { 73 | // Answer: This is a value/pointer semantic 74 | fmt.Printf("%s: AVG[.%.f]\n", ps[i].name, ps[i].average()*1000) 75 | } 76 | 77 | fmt.Println("\n\n") 78 | // Why did I not choose this form? 79 | for _, p := range ps { 80 | // Answer: This is a copy semantic 81 | fmt.Printf("%s: AVG[.%.f]\n", p.name, p.average()*1000) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /language/pointers/README.md: -------------------------------------------------------------------------------- 1 | ## Pointers 2 | 3 | Pointers provide a way to share data across function boundaries. Having the ability to share and reference data with a pointer provides flexbility. It also helps our programs minimize the amount of memory they need and can add some extra performance. 4 | 5 | ## Notes 6 | 7 | * Use pointers to share data. 8 | * Values in Go are always pass by value. 9 | * "Value of", what's in the box. "Address of" ( **&** ), where is the box. 10 | * The (*) operator declares a pointer variable and the "Value that the pointer points to". 11 | -------------------------------------------------------------------------------- /language/pointers/exercise1/README.md: -------------------------------------------------------------------------------- 1 | ### Exercise 1 2 | 3 | **Part A** Declare and initialize a variable of type int with the value of 20. Display the _address of_ and _value of_ the variable. 4 | 5 | **Part B** Declare and initialize a pointer variable of type int that points to the last variable you just created. Display the _address of_ , _value of_ and the _value that the pointer points to_. 6 | 7 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/ZiVZzVkMqk)) | 8 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/ARXt9Ddawc)) 9 | 10 | Solution: 11 | ```go 12 | import "fmt" 13 | 14 | func main() { 15 | 16 | // Declare an integer variable with the value of 20. 17 | value := 20 18 | 19 | // Display the address of and value of the variable. 20 | fmt.Println("Address Of:", &value, "Value Of:", value) 21 | 22 | // Declare a pointer variable of type int. Assign the 23 | // address of the integer variable above. 24 | p := &value 25 | 26 | // Display the address of, value of and the value the pointer 27 | // points to. 28 | fmt.Println("Address Of:", &p, "Value Of:", p, "Points To:", *p) 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /language/pointers/exercise1/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare and initialize a variable of type int with the value of 20. Display 5 | // the _address of_ and _value of_ the variable. 6 | // 7 | // Declare and initialize a pointer variable of type int that points to the last 8 | // variable you just created. Display the _address of_ , _value of_ and the 9 | // _value that the pointer points to_. 10 | package main 11 | 12 | // Add imports. 13 | import "fmt" 14 | 15 | func main() { 16 | 17 | // Declare an integer variable with the value of 20. 18 | num := 20 19 | 20 | // Display the address of and value of the variable. 21 | fmt.Printf("Address of: %+v\n", &num) 22 | fmt.Printf("Value of: %+v\n", num) 23 | 24 | // Declare a pointer variable of type int. Assign the 25 | // address of the integer variable above. 26 | // var numPointer *int 27 | numPointer := &num 28 | 29 | // Display the address of, value of and the value the pointer 30 | // points to. 31 | fmt.Printf("\naddress: %+v\n", numPointer) 32 | fmt.Printf("value: %+v\n", *numPointer) 33 | fmt.Printf("points to: %+v\n", *numPointer) 34 | } 35 | -------------------------------------------------------------------------------- /language/pointers/exercise2/README.md: -------------------------------------------------------------------------------- 1 | ### Exercise 2 2 | 3 | Declare a struct type and create a value of this type. Declare a function that can change the value of some field in this struct type. Display the value before and after the call to your function. 4 | 5 | [Template](exercises/template2/template2.go) ([Go Playground](http://play.golang.org/p/qT4JMQDzpD)) | 6 | [Answer](exercises/exercise2/exercise2.go) ([Go Playground](http://play.golang.org/p/DS8DZnEg6i)) 7 | 8 | Solution: 9 | ```go 10 | package main 11 | 12 | import "fmt" 13 | 14 | // user represents a user in the system. 15 | type user struct { 16 | name string 17 | email string 18 | accessLevel int 19 | } 20 | 21 | func main() { 22 | 23 | // Create a variable of type user and initialize each field. 24 | bill := user{ 25 | name: "Bill", 26 | email: "bill@ardanlabs.com", 27 | accessLevel: 1, 28 | } 29 | 30 | // Display the value of the accessLevel field. 31 | fmt.Println("access:", bill.accessLevel) 32 | 33 | // Share the bill variable with the accessLevel function 34 | // along with a value to update the accessLevel field with. 35 | accessLevel(&bill, 10) 36 | 37 | // Display the value of the accessLevel field again. 38 | fmt.Println("access:", bill.accessLevel) 39 | } 40 | 41 | // accessLevel changes the value of the users access level. 42 | func accessLevel(u *user, accessLevel int) { 43 | 44 | // Set of value of the accessLevel field to the value 45 | // that is passed in. 46 | u.accessLevel = accessLevel 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /language/pointers/exercise2/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare a struct type and create a value of this type. Declare a function 5 | // that can change the value of some field in this struct type. Display the 6 | // value before and after the call to your function. 7 | package main 8 | 9 | // Add imports. 10 | import ( 11 | "fmt" 12 | ) 13 | 14 | // Declare a type named user. 15 | type user struct { 16 | name string 17 | age int 18 | male bool 19 | } 20 | 21 | // Create a function that changes the value of one of the user fields. 22 | func changeName(u *user, name string) { 23 | u.name = name 24 | } 25 | 26 | func main() { 27 | // Create a variable of type user and initialize each field. 28 | u := user{"jack", 30, true} 29 | 30 | // Display the value of the variable. 31 | fmt.Printf("u.name %+v\nu.age %+v\nu.male %+v", u.name, u.age, u.male) 32 | 33 | // Share the variable with the function you declared above. 34 | changeName(&u, "Jill") 35 | 36 | // Display the value of the variable. 37 | fmt.Println("\n\nu.name", u.name) 38 | } 39 | -------------------------------------------------------------------------------- /language/slices/README.md: -------------------------------------------------------------------------------- 1 | ## Slices - Arrays, Slices and Maps 2 | 3 | Slices are an incredibly important data structure in Go. They form the basis for how we manage and manipulate data in a flexible, performant and dynamic way. It is incredibly important for all Go programmers to learn how to uses slices. 4 | 5 | ## Notes 6 | 7 | * Slices are like dynamic arrays with special and built-in functionality. 8 | * There is a difference between a slices length and capacity and they each service a purpose. 9 | * Slices allow for multiple "views" of the same underlying array. 10 | * Slices can grow through the use of the built-in function append. 11 | 12 | ### Exercise 1 13 | 14 | **Part A** Declare a nil slice of integers. Create a loop that appends 10 values to the slice. Iterate over the slice and display each value. 15 | 16 | **Part B** Declare a slice of five strings and initialize the slice with string literal values. Display all the elements. Take a slice of index one and two and display the index position and value of each element in the new slice. 17 | 18 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/sE06PRtw7h)) | 19 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/3WKISOXA-L)) 20 | 21 | Solution: 22 | ```go 23 | package main 24 | 25 | import "fmt" 26 | 27 | func main() { 28 | 29 | // Declare a nil slice of integers. 30 | var numbers []int 31 | 32 | // Append numbers to the slice. 33 | for i := 0; i < 10; i++ { 34 | numbers = append(numbers, i*10) 35 | } 36 | 37 | // Display each value. 38 | for _, number := range numbers { 39 | fmt.Println(number) 40 | } 41 | 42 | // Declare a slice of strings. 43 | names := []string{"Bill", "Lisa", "Jim", "Cathy", "Beth"} 44 | 45 | // Display each index position and name. 46 | for i, name := range names { 47 | fmt.Printf("Index: %d Name: %s\n", i, name) 48 | } 49 | 50 | // Take a slice of index 1 and 2. 51 | slice := names[1:3] 52 | 53 | // Display the value of the new slice. 54 | for i, name := range slice { 55 | fmt.Printf("Index: %d Name: %s\n", i, name) 56 | } 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /language/slices/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare a nil slice of integers. Create a loop that appends 10 values to the 5 | // slice. Iterate over the slice and display each value. 6 | // 7 | // Declare a slice of five strings and initialize the slice with string literal 8 | // values. Display all the elements. Take a slice of index one and two 9 | // and display the index position and value of each element in the new slice. 10 | package main 11 | 12 | // Add imports. 13 | import "fmt" 14 | 15 | func main() { 16 | 17 | // Declare a nil slice of integers. 18 | var sliceInts []int 19 | 20 | // Appends numbers to the slice. 21 | sliceInts = append(sliceInts, 1, 2, 3) 22 | 23 | // Display each value in the slice. 24 | fmt.Println("sliceInts[0]", sliceInts[0]) 25 | fmt.Println("sliceInts[1]", sliceInts[1]) 26 | fmt.Println("sliceInts[2]", sliceInts[2]) 27 | 28 | // Declare a slice of strings and populate the slice with names. 29 | sliceStrs := []string{"a", "b", "c"} 30 | 31 | fmt.Println("\n\n") 32 | 33 | // Display each index position and slice value. 34 | for i, v := range sliceStrs { 35 | fmt.Printf("Index = %+v Value = %s\n", i, v) 36 | } 37 | 38 | // Take a slice of index 1 and 2 of the slice of strings. 39 | sliceTwo := sliceStrs[1:3] 40 | 41 | fmt.Println("\n\n") 42 | 43 | // Display each index position and slice values for the new slice. 44 | for i, v := range sliceTwo { 45 | fmt.Printf("Index = %+v Value = %s\n", i, v) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /language/struct_types/README.md: -------------------------------------------------------------------------------- 1 | ## Struct Types 2 | 3 | Struct types are a way of creating complex types that group fields of data together. They are a great way of organizing and sharing the different aspects of the data your program consumes. 4 | 5 | A computer architecture’s potential performance is determined predominantly by its word length (the number of bits that can be processed per access) and, more importantly, memory size, or the number of words that it can access. 6 | 7 | ## Notes 8 | 9 | * We can use the struct literal form to initialize a value from a struct type. 10 | * The dot (.) operator allows us to access individual field values. 11 | * We can create anonymous structs. 12 | 13 | ### Exercise 1 14 | 15 | **Part A:** Declare a struct type to maintain information about a user (name, email and age). Create a value of this type, initialize with values and display each field. 16 | 17 | **Part B:** Declare and initialize an anonymous struct type with the same three fields. Display the value. 18 | 19 | [Template](exercises/template1/template1.go) ([Go Playground](http://play.golang.org/p/PvQKHgf9jZ)) | 20 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](http://play.golang.org/p/8CtSrnTp-1)) 21 | 22 | Solution: 23 | ```go 24 | package main 25 | 26 | import "fmt" 27 | 28 | // user represents a user in the system. 29 | type user struct { 30 | name string 31 | email string 32 | age int 33 | } 34 | 35 | func main() { 36 | 37 | // Declare variable of type user and init using a struct literal. 38 | bill := user{ 39 | name: "Bill", 40 | email: "bill@ardanlabs.com", 41 | age: 45, 42 | } 43 | 44 | // Display the field values. 45 | fmt.Println("Name", bill.name) 46 | fmt.Println("Email", bill.email) 47 | fmt.Println("Age", bill.age) 48 | 49 | // Declare a variable using an anonymous struct. 50 | ed := struct { 51 | name string 52 | email string 53 | age int 54 | }{ 55 | name: "Ed", 56 | email: "ed@ardanlabs.com", 57 | age: 46, 58 | } 59 | 60 | // Display the field values. 61 | fmt.Println("Name", ed.name) 62 | fmt.Println("Email", ed.email) 63 | fmt.Println("Age", ed.age) 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /language/struct_types/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare a struct type to maintain information about a user (name, email and age). 5 | // Create a value of this type, initialize with values and display each field. 6 | // 7 | // Declare and initialize an anonymous struct type with the same three fields. Display the value. 8 | package main 9 | 10 | // Add imports. 11 | import "fmt" 12 | 13 | // Add user type and provide comment. 14 | type user struct { 15 | name string 16 | email string 17 | age uint8 18 | } 19 | 20 | func main() { 21 | 22 | // Declare variable of type user and init using a struct literal. 23 | u := user{"Jack", "jack@email.env", 34} 24 | 25 | // Display the field values. 26 | fmt.Printf("{\n\tname: %+v,\n\temail: %+v,\n\tage: %+v,\n}\n", u.name, u.email, u.age) 27 | 28 | // Declare a variable using an anonymous struct. 29 | u2 := struct { 30 | name string 31 | email string 32 | age uint8 33 | }{"jill", "jill@email.env", 21} 34 | 35 | // Display the field values. 36 | fmt.Printf("{\n\tname: %+v,\n\temail: %+v,\n\tage: %+v,\n}", u2.name, u2.email, u2.age) 37 | } 38 | -------------------------------------------------------------------------------- /language/variables/README.md: -------------------------------------------------------------------------------- 1 | ## Variables 2 | 3 | Variables are at the heart of the language and provide the ability to read from and write to memory. In Go, access to memory is type safe. This means the compiler takes type seriously and will not allow us to use variables outside the scope of how they are declared. 4 | 5 | ## Notes 6 | 7 | * The purpose of all programs and all parts of those programs it to transform data from one form to the other. 8 | * Code primarily allocates, reads and writes to memory. 9 | * Understanding type is crucial to writing good code and understanding code. 10 | * If you don't understand the data, you don't understand the problem. 11 | * You understand the problem better by understanding the data. 12 | * When variables are being declared to their zero value, use the keyword var. 13 | * When variables are being declared and initialized, use the short variable declaration operator. 14 | 15 | ### Exercise 1 16 | 17 | **Part A:** Declare three variables that are initialized to their zero value and three declared with a literal value. Declare variables of type string, int and bool. Display the values of those variables. 18 | 19 | **Part B:** Declare a new variable of type float32 and initialize the variable by converting the literal value of Pi (3.14). 20 | 21 | [Template](exercises/template1/template1.go) ([Go Playground](https://play.golang.org/p/JIgjb3Ty3e)) | 22 | [Answer](exercises/exercise1/exercise1.go) ([Go Playground](https://play.golang.org/p/wNjayRMEcM)) 23 | 24 | Solution: 25 | ```go 26 | package main 27 | 28 | import "fmt" 29 | 30 | func main() { 31 | 32 | // Declare variables that are set to their zero value. 33 | var age int 34 | var name string 35 | var legal bool 36 | 37 | // Display the value of those variables. 38 | fmt.Println(age) 39 | fmt.Println(name) 40 | fmt.Println(legal) 41 | 42 | // Declare variables and initialize. 43 | // Using the short variable declaration operator. 44 | month := 10 45 | dayOfWeek := "Tuesday" 46 | happy := true 47 | 48 | // Display the value of those variables. 49 | fmt.Println(month) 50 | fmt.Println(dayOfWeek) 51 | fmt.Println(happy) 52 | 53 | // Perform a type conversion. 54 | pi := float32(3.14) 55 | 56 | // Display the value of that variable. 57 | fmt.Printf("%T [%v]\n", pi, pi) 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /language/variables/main.go: -------------------------------------------------------------------------------- 1 | // All material is licensed under the Apache License Version 2.0, January 2004 2 | // http://www.apache.org/licenses/LICENSE-2.0 3 | 4 | // Declare three variables that are initialized to their zero value and three 5 | // declared with a literal value. Declare variables of type string, int and 6 | // bool. Display the values of those variables. 7 | // 8 | // Declare a new variable of type float32 and initialize the variable by 9 | // converting the literal value of Pi (3.14). 10 | package main 11 | 12 | import "fmt" 13 | 14 | // main is the entry point for the application. 15 | func main() { 16 | 17 | // Declare variables that are set to their zero value. 18 | var i int 19 | var f float64 20 | var s string 21 | var b bool 22 | 23 | // Display the value of those variables. 24 | fmt.Printf("i = %+v\n", i) 25 | fmt.Printf("f = %+v\n", f) 26 | fmt.Printf("s = %+v\n", s) 27 | fmt.Printf("b = %+v\n", b) 28 | 29 | // Declare variables and initialize. 30 | // Using the short variable declaration operator. 31 | ii := 15 32 | ff := 3.14159265359 33 | ss := "I've got no strings" 34 | bb := true 35 | fmt.Println("=========================") 36 | // Display the value of those variables. 37 | fmt.Printf("ii = %+v\n", ii) 38 | fmt.Printf("ff = %+v\n", ff) 39 | fmt.Printf("ss = %+v\n", ss) 40 | fmt.Printf("bb = %+v\n", bb) 41 | 42 | // Perform a type conversion. 43 | pi := float32(ff) 44 | 45 | // Display the value of that variable. 46 | fmt.Println("=========================") 47 | fmt.Printf("pi = %+v\n", pi) 48 | } 49 | --------------------------------------------------------------------------------