├── concurrency ├── channels │ ├── range │ │ └── main.go │ ├── simple-example │ │ └── main.go │ ├── worker │ │ └── main.go │ ├── multi-worker-with-cancel │ │ └── main.go │ └── queue-service │ │ └── main.go └── wait-group │ └── main.go └── README.md /concurrency/channels/range/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func printInput(c <-chan string) { 6 | // range over c until it is closed 7 | for v := range c { 8 | fmt.Println(v) 9 | } 10 | } 11 | 12 | func main() { 13 | c := make(chan string) 14 | defer close(c) 15 | go printInput(c) 16 | c <- "Hello" 17 | c <- "World" 18 | } 19 | -------------------------------------------------------------------------------- /concurrency/channels/simple-example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func someExpensiveTask(done chan<- struct{}) { 10 | fmt.Println("Starting expensive task") 11 | // Sleep for a random amount of time, up to 5 seconds 12 | time.Sleep(time.Second * time.Duration(rand.Intn(5))) 13 | fmt.Println("Expensive task finished") 14 | done <- struct{}{} 15 | } 16 | 17 | func main() { 18 | done := make(chan struct{}) 19 | go someExpensiveTask(done) 20 | // do other stuff 21 | <-done 22 | fmt.Println("Done") 23 | } 24 | -------------------------------------------------------------------------------- /concurrency/channels/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type worker struct { 6 | id int // id to track the worker 7 | jobs <-chan int // read only jobs chan 8 | results chan<- string // write only results chan 9 | } 10 | 11 | func newWorker(id int, jobs <-chan int, results chan<- string) *worker { 12 | return &worker{id, jobs, results} 13 | } 14 | 15 | func (w *worker) doWork() { 16 | // read from jobs until jobs is closed 17 | for job := range w.jobs { 18 | // Print the worker and job id 19 | w.results <- fmt.Sprintf("worker id: %d, job: %d", w.id, job) 20 | } 21 | } 22 | 23 | func main() { 24 | numJobs := 10 25 | 26 | jobs := make(chan int) 27 | results := make(chan string) 28 | 29 | numWorkers := 3 30 | // Create and start the workers 31 | for i := 0; i < numWorkers; i++ { 32 | w := newWorker(i, jobs, results) 33 | go w.doWork() 34 | } 35 | 36 | // Fill up the jobs chan 37 | go func() { 38 | for i := 0; i < numJobs; i++ { 39 | jobs <- i 40 | } 41 | }() 42 | 43 | // Read the results 44 | for i := 0; i < numJobs; i++ { 45 | // read result 46 | res := <-results 47 | fmt.Println(res) 48 | // notice how jobs are processed out of order, as workers have availibility 49 | } 50 | 51 | close(jobs) 52 | } 53 | -------------------------------------------------------------------------------- /concurrency/wait-group/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type resultsList struct { 11 | values []string 12 | mu sync.Mutex 13 | } 14 | 15 | func getURL(wg *sync.WaitGroup, results *resultsList, url string) { 16 | 17 | min := 50 // sleep for min of 50ms 18 | max := 300 // sleep for max of 300ms 19 | sleep := rand.Intn(max-min) + min // calc the sleep time 20 | 21 | time.Sleep(time.Duration(sleep)) 22 | 23 | // We could use channels and some sort of listener to achieve concurrency safe writes, but a mutex will be fine. 24 | results.mu.Lock() 25 | results.values = append(results.values, url) 26 | results.mu.Unlock() 27 | // Decrement the counter when the goroutine completes. 28 | wg.Done() 29 | 30 | } 31 | 32 | // A more understand Task.WhenAll() 33 | func main() { 34 | resultsList := &resultsList{values: []string{}, mu: sync.Mutex{}} 35 | 36 | var wg sync.WaitGroup 37 | var urls = []string{ 38 | "http://www.golang.org/", 39 | "http://www.google.com/", 40 | "http://www.bing.com/", 41 | "http://www.fakeblock.com/", 42 | "http://www.facebook.com/", 43 | } 44 | 45 | for _, url := range urls { 46 | // Increment the WaitGroup counter. 47 | wg.Add(1) 48 | // Launch a goroutine to fetch the URL. 49 | go getURL(&wg, resultsList, url) 50 | } 51 | // Wait for all workers to complete. 52 | wg.Wait() 53 | 54 | // Print the results 55 | resultsList.mu.Lock() 56 | fmt.Println(resultsList.values) 57 | resultsList.mu.Unlock() 58 | 59 | } 60 | -------------------------------------------------------------------------------- /concurrency/channels/multi-worker-with-cancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "time" 9 | ) 10 | 11 | type workerService struct { 12 | id int // for tracking the worker 13 | request chan int // for taking in a request 14 | results chan<- int // for writing the results 15 | ctx context.Context // for being able to process cancelation 16 | cancel context.CancelFunc // for canceling the worker. Calling cancel() writes to the "Done" chan 17 | } 18 | 19 | func newWorkerService(ctx context.Context, cancel context.CancelFunc, id int, results chan<- int) *workerService { 20 | return &workerService{ctx: ctx, cancel: cancel, id: id, request: make(chan int), results: results} 21 | } 22 | 23 | // meant to simulate slow and/or seemingly random services 24 | func (w *workerService) doWork() { 25 | min := 50 // sleep for min of 50ms 26 | max := 300 // sleep for max of 300ms 27 | sleep := rand.Intn(max-min) + min // calc the sleep time 28 | 29 | // block until we get a request 30 | <-w.request 31 | // block for a certain duration (to simulate latency) or until cancel is called. 32 | select { 33 | case <-time.After(time.Millisecond * time.Duration(sleep)): 34 | // echo back the id of the worker who completed the request 35 | w.results <- w.id 36 | return 37 | case <-w.ctx.Done(): 38 | fmt.Printf("worker: %d canceled\n", w.id) 39 | return 40 | } 41 | } 42 | 43 | // simulate making request to multiple instances of the same service and canceling the request when one returns 44 | func main() { 45 | // Setup results chan 46 | results := make(chan int) 47 | // Set up n workers 48 | n := 5 49 | // Track the workers 50 | workers := []*workerService{} 51 | for i := 0; i < n; i++ { 52 | ctx := context.Background() 53 | ctx, cancel := context.WithCancel(ctx) 54 | w := newWorkerService(ctx, cancel, i, results) 55 | workers = append(workers, w) 56 | go w.doWork() 57 | } 58 | start := time.Now() // Track how long the request takes 59 | 60 | // propigate a request to all the workers 61 | for _, w := range workers { 62 | w.request <- 1 63 | } 64 | 65 | // get result 66 | workerID := <-results 67 | 68 | // cancel workers 69 | for _, w := range workers { 70 | w.cancel() 71 | } 72 | 73 | elapsed := time.Since(start) 74 | log.Printf("Request took %s and was completed by worker with ID: %d", elapsed, workerID) 75 | } 76 | -------------------------------------------------------------------------------- /concurrency/channels/queue-service/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/google/uuid" 8 | ) 9 | 10 | //NOTE: There are likely some race conditions with subscribe (subs) and unsubscribe (subs). 11 | // Could use mutex to fix this. 12 | 13 | type queueService struct { 14 | subs map[uuid.UUID]*subscriber 15 | } 16 | 17 | func newQueueService() *queueService { 18 | return &queueService{subs: make(map[uuid.UUID]*subscriber)} 19 | } 20 | 21 | func (qs *queueService) subscribe(topic string) *subscriber { 22 | s := newSubscriber(topic) 23 | qs.subs[s.id] = s 24 | // Listen for unsubscribe 25 | go func() { 26 | <-s.u 27 | delete(qs.subs, s.id) 28 | }() 29 | return s 30 | } 31 | 32 | func (qs *queueService) publish(msg msg) { 33 | for _, s := range qs.subs { 34 | if s.topic == msg.topic { 35 | go s.publish(msg) 36 | } 37 | } 38 | } 39 | 40 | type msg struct { 41 | topic string 42 | payload interface{} 43 | } 44 | 45 | type subscriber struct { 46 | id uuid.UUID 47 | topic string 48 | m chan msg //chan that messages will be published on 49 | u chan struct{} // unsubscribe chan 50 | } 51 | 52 | func newSubscriber(topic string) *subscriber { 53 | return &subscriber{id: uuid.New(), topic: topic, m: make(chan msg), u: make(chan struct{})} 54 | } 55 | 56 | func (s *subscriber) unsubscribe() { 57 | s.u <- struct{}{} 58 | } 59 | 60 | func (s *subscriber) publish(msg msg) { 61 | s.m <- msg 62 | } 63 | 64 | func listenForMessages(s *subscriber) { 65 | for { 66 | select { 67 | case msg := <-s.m: 68 | fmt.Printf("id: %s, msg: %s\n", s.id.String(), msg.payload) 69 | case <-s.u: 70 | return 71 | } 72 | 73 | } 74 | } 75 | 76 | func main() { 77 | qs := newQueueService() 78 | 79 | dogs1 := qs.subscribe("dogs") 80 | dogs2 := qs.subscribe("dogs") 81 | cats1 := qs.subscribe("cats") 82 | cats2 := qs.subscribe("cats") 83 | 84 | // Listen for n messages 85 | go listenForMessages(dogs1) 86 | go listenForMessages(dogs2) 87 | go listenForMessages(cats1) 88 | go listenForMessages(cats2) 89 | 90 | n := 10 91 | for i := 0; i < n; i++ { 92 | if i%2 == 0 { 93 | qs.publish(msg{topic: "dogs", payload: "Doggy"}) 94 | continue 95 | } 96 | qs.publish(msg{topic: "cats", payload: "Cats"}) 97 | } 98 | 99 | time.Sleep(time.Second) 100 | 101 | dogs1.unsubscribe() 102 | dogs2.unsubscribe() 103 | cats1.unsubscribe() 104 | cats2.unsubscribe() 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Overview 2 | 3 | ## Resources 4 | 5 | - Much of this overview is taken from [A Tour of Go](https://tour.golang.org/welcome/1) 6 | - [Go Time Podcast](https://changelog.com/gotime) 7 | 8 | ## Go as a language 9 | 10 | - Originally developed by Google to address criticisms of other languages. The designers were primarily motivated by their shared dislike of C++. 11 | - Statically typed 12 | - Similar to C (but memory safe, with garbage collection and structural typing) 13 | - Great for concurrency 14 | - v1.0 was publicly released in March 2012 15 | 16 | ## Why I think you should learn (and use) Go 17 | 18 | - Readability is high (i.e. it's easy to reason what a program is meant to do) 19 | - Cross platform - Write code once, compile to your target and run. 20 | - It's easy to learn and use, but is still powerful 21 | - If you have some programming experience you could learn the syntax in about a day and start being productive in it right after. 22 | - Like any language, you'll become more productive as you learn the standard library and other popular packages 23 | - The community is awesome and is growing every day 24 | - Makes concurrency easy (easier) - but be careful not to go crazy with this at first 25 | - Concurrency is also easier to reason about (IMO) than that of dotnet's async-await 26 | - Write your test files along side you package files (no testing framework needed). Test package is included in the standard library. 27 | 28 | ## What is Go good for? 29 | 30 | Go is often called the "Devops" language, but that's a myth (though there is a ton of devops tooling written in Go). It's good for just about anything you want to do. 31 | 32 | - Web Backends 33 | - Helpful Packages: 34 | - [buffalo](https://gobuffalo.io/en/) 35 | - [gorilla](https://github.com/gorilla) 36 | - [gin](https://github.com/gin-gonic/gin) 37 | - Cli Tools 38 | - Docker, K8s, and many more are written in Go 39 | - Helpful Packages 40 | - [cobra](https://github.com/spf13/cobra) 41 | - MicroControllers 42 | - [tiny go](https://tinygo.org/) - Go for small spaces 43 | - Web Assembly 44 | - [Getting started with WASM using Go](https://github.com/golang/go/wiki/WebAssembly) 45 | - "Serverless" 46 | - AWS Lamda, GCP Functions, others 47 | - Some Links 48 | - [AWS Lamda](https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html) 49 | - [Google Cloud Platform Functions](https://cloud.google.com/functions/docs/concepts/go-runtime) 50 | - Anywhere that you need a scripting language 51 | - Go can be compiled to target nearly any operating system, so it's perfect for scripting. 52 | - Just about anything you can think of. 53 | 54 | ## What does a Go program look like? 55 | 56 | - Go programs are made up of packages 57 | - Programs start running in main() 58 | - Builds produce a single binary 59 | 60 | ```go 61 | package main 62 | 63 | import "fmt" 64 | 65 | func main() { 66 | fmt.Println("Hello, SDG!") 67 | } 68 | ``` 69 | 70 | ### Helpful tips 71 | 72 | - Packages named after folders (typically) 73 | - Files in the same folder are all a part of the same package (unless it is named [package name]\_test) 74 | - Downloading packages 75 | - `go get ` 76 | - Running a go program 77 | - `go run .go` 78 | - executable programs start in main, so the file could be server.go, main.go, foobar.go, etc. so long as it has the main function 79 | - typically main.go 80 | - Building binary 81 | - `go build .` 82 | - Running a binary 83 | - `.\` 84 | - Use modules for version control 85 | - `go mod init ` 86 | - [Using Go Modules](https://blog.golang.org/using-go-modules) 87 | - Errors, not exceptions 88 | 89 | - Go doesn't have exceptions, it has errors, which are explicitly returned. 90 | - IMO, this is better than try-catch-finally, because it forces you to acknowledge and handle or explicitly ignore the error where as languages that allow for exceptions can obfuscate the exceptions that are thrown if they are not documented. 91 | 92 | ```go 93 | package data 94 | 95 | import ( 96 | "fmt" 97 | "github.com/pkg/errors" 98 | ) 99 | 100 | // For custom errors, can use errors.New() 101 | // Good idea to export these so that they can be checked for 102 | 103 | // ErrSomeCustom is a custom error 104 | var ErrSomeCustom = errors.New("Some Custom Error") 105 | 106 | func getPersonById(id string) *person, error { 107 | person, err := db.GetPerson(id) 108 | if err != nil { 109 | return nil, errors.Wrap(err, fmt.Sprintf("Could not get person with id: %s from database", id)) 110 | } 111 | return person, nil 112 | } 113 | ``` 114 | 115 | - Go doesn't have generics [yet](https://blog.golang.org/generics-next-step). Because of this an empty interface is often used in a function signature so that it can take in any type (every type meets the requirements of any empty interface) 116 | ```go 117 | func Print(a interface{}) (n int, err error){} 118 | func Println(a ...interface{}) (n int, err error){} 119 | ``` 120 | - Hopefully we'll have generics soon. 121 | 122 | ## Useful Packages 123 | 124 | - Database 125 | - [Migrate](https://pkg.go.dev/github.com/golang-migrate/migrate/v4@v4.14.1?utm_source=gopls) 126 | - Useful for handling database migrations 127 | - Web 128 | - [gqlgen](https://github.com/99designs/gqlgen) 129 | - Generate boilerplate code for GraphQL based on your schema 130 | - Environment configuration 131 | - [godotenv](https://pkg.go.dev/github.com/joho/godotenv@v1.3.0?utm_source=gopls) 132 | - Useful for pulling in .env files 133 | - [envconfig](https://pkg.go.dev/github.com/kelseyhightower/envconfig@v1.4.0?utm_source=gopls) 134 | - Useful for decoding environment variables 135 | - Testing 136 | - [dockertest](https://pkg.go.dev/github.com/ory/dockertest@v3.3.5+incompatible?utm_source=gopls) 137 | - Useful for running docker containers during test 138 | - I use this to spin up a postgres container, then run my migrations on it when testing my database access. Testing against the real thing is alway preferable to testing against some in memory version of it (to me anyways). 139 | - [require](https://pkg.go.dev/github.com/stretchr/testify@v1.6.1/require?utm_source=gopls) 140 | - Useful for assertions. There's also a package called assert that's exactly the same, except that require will stop your tests as soon as you have a failure. 141 | - [mock](https://pkg.go.dev/github.com/stretchr/testify@v1.6.1/mock?utm_source=gopls) 142 | - Useful for mocking. 143 | 144 | # The Language 145 | 146 | Note, this section has a lot of sudo code for brevity 147 | 148 | ## Syntax 149 | 150 | - Go only has 25 keywords 151 | - C# has 110 152 | - JavaScript has 64 153 | - Go has 45 operators and punctuation 154 | 155 | ### package, import, func 156 | 157 | ```go 158 | // define package that file belongs to 159 | package main 160 | 161 | // import packages that source file depends on 162 | import ( 163 | "fmt" 164 | "math/rand" 165 | ) 166 | 167 | // function declaration 168 | func main() { 169 | fmt.Println("The random number is", rand.Intn(10)) 170 | } 171 | ``` 172 | 173 | ### var, const 174 | 175 | ```go 176 | // variable declaration 177 | var y = 1 178 | // or 179 | x := 2 180 | 181 | // constant declaration 182 | const z = 3 183 | ``` 184 | 185 | ### pointers 186 | 187 | ```go 188 | package main 189 | 190 | import "fmt" 191 | 192 | func main() { 193 | // The type *T is a pointer to a T value. Its zero value is nil. 194 | // The & operator generates a pointer to its operand. 195 | // The * operator denotes the pointer's underlying value. 196 | // Go has no pointer arithmetic. 197 | 198 | i, j := 42, 2701 199 | 200 | p := &i // point to i 201 | fmt.Println(*p) // read i through the pointer - prints 42 202 | *p = 21 // set i through the pointer 203 | fmt.Println(i) // see the new value of i - prints 21 204 | 205 | p = &j // point to j 206 | *p = *p / 37 // divide j through the pointer 207 | fmt.Println(j) // see the new value of j - prints 73 208 | } 209 | ``` 210 | 211 | ### struct 212 | 213 | ```go 214 | package main 215 | 216 | import "fmt" 217 | 218 | // Struct declaration 219 | // A struct is a collection of fields. 220 | type pet struct { 221 | name string 222 | age int 223 | } 224 | 225 | func newPet(name string, age int) *pet { 226 | p := person{name: name, age: age} 227 | return &p 228 | } 229 | 230 | func main() { 231 | // How to create new structs: 232 | fmt.Println(pet{"Max", 5}) //{Max 5} 233 | fmt.Println(pet{name: "Buddy", age: 10}) //{Buddy 10} 234 | // Omited fields will be zero valued 235 | fmt.Println(pet{name: "Shadow"}) //{Shadow 0} 236 | // An & prefix yields a pointer to the struct. 237 | fmt.Println(&pet{name: "Honey", age: 14}) //&{Honey 14} 238 | 239 | // access struct fields with a dot 240 | fluffy := pet{name: "Fluffy", age: 7} 241 | fmt.Println(fluffy.name) //Fluffy 242 | 243 | // You can also use dots with struct pointers - the pointers are automatically dereferenced. 244 | fluffyPointer := &fluffy 245 | fmt.Println(fluffyPointer.age) 246 | 247 | // Structs are mutable 248 | fluffyPointer.age = 8 249 | fmt.Println(fluffyPointer.age) //8 250 | } 251 | ``` 252 | 253 | ### interfaces 254 | 255 | ```go 256 | package messenger 257 | 258 | // Note: Capitalized structs, interfaces, consts, vars and functions are exported for use in other packages. Exported fields should be commented. 259 | 260 | // Mesage type 261 | type Message struct { 262 | from string 263 | to string 264 | content string 265 | } 266 | 267 | // Interface declaration 268 | // An interface type is defined as a set of method signatures. 269 | // MessageService sends messages 270 | type MessageService interface { 271 | SendMessage(msg message) error 272 | } 273 | 274 | // Example interface implementation: 275 | // To implement an interface, implement all the methods in the interface 276 | // Notice how the implementation is implicit, not explicit 277 | type messageService struct { 278 | client *SES // AWS Simple Email Service 279 | } 280 | 281 | // Note: This is sudo code 282 | func (ms *messageService) SendMessage(msg) error { 283 | err := ms.client.SendMessage(msg) 284 | return err 285 | } 286 | ``` 287 | 288 | ## Arrays, Slices, Maps 289 | 290 | ### Arrays 291 | 292 | - The type [n]T is an array of n values of type T. 293 | 294 | ```go 295 | var a [2]string 296 | a[0] = "Hello" 297 | a[1] = "World" 298 | fmt.Println(a[0], a[1]) //Hello World 299 | fmt.Println(a) //[Hello World] 300 | 301 | primes := [6]int{2, 3, 5, 7, 11, 13} 302 | fmt.Println(primes) //[2 3 5 7 11 13] 303 | ``` 304 | 305 | ### Slices 306 | 307 | - For a more in depth overview of slices, check out slices in [A Tour of Go](https://tour.golang.org/moretypes/7) 308 | - The type []T is a slice with elements of type T. 309 | - Slices are references to the underlying array. Changing the elements of a slice modifies the corresponding elements of its underlying array. Other slices that share the same underlying array will see those changes. 310 | - Form slice like this: a[low : high] 311 | 312 | ```go 313 | primes := [6]int{2, 3, 5, 7, 11, 13} 314 | var s []int = primes[1:4] 315 | fmt.Println(s) //[3 5 7] 316 | ``` 317 | 318 | ```go 319 | nums := []int{} 320 | // append to a slice 321 | nums = append(nums, 1) 322 | nums = append(nums, 2, 3) 323 | fmt.Println(nums) //[1 2 3] 324 | ``` 325 | 326 | - Slices have length and capacity 327 | - Zero value of a slice is nil 328 | 329 | ### Maps 330 | 331 | - Maps are similar to dictionaries 332 | - They map keys to values 333 | - Zero value of a map is nil 334 | - The make function returns a map of the given type, initialized and ready for use. 335 | 336 | ```go 337 | type Vertex struct { 338 | Lat, Long float64 339 | } 340 | 341 | var m map[string]Vertex 342 | 343 | func main() { 344 | m = make(map[string]Vertex) 345 | m["Bell Labs"] = Vertex{ 346 | 40.68433, -74.39967, 347 | } 348 | fmt.Println(m["Bell Labs"]) 349 | } 350 | 351 | // Using map literal 352 | var m = map[string]Vertex{ 353 | "Bell Labs": {40.68433, -74.39967}, 354 | "Google": {37.42202, -122.08408}, 355 | } 356 | 357 | // insert or update an element 358 | m[key] = elem 359 | 360 | // retrieve an element 361 | elem = m[key] 362 | 363 | // delete an element 364 | delete(m, key) 365 | 366 | // check that an element exists in the map 367 | // If key is in m, ok is true. If not, ok is false. 368 | elem, ok = m[key] 369 | ``` 370 | 371 | ## Control Flow 372 | 373 | ### For loop 374 | 375 | ```go 376 | // A familiar for loop (init, exit condition, post) 377 | sum := 0 378 | for i := 0; i < 10; i++ { 379 | sum += i 380 | } 381 | fmt.Println(sum) //45 382 | 383 | // init and post statements are optional 384 | // (i.e. for is go's while) 385 | sum = 1 386 | for sum < 1000 { 387 | sum += sum 388 | } 389 | fmt.Println(sum) //1024 390 | 391 | // Loop forever by omiting the exit condition 392 | for { 393 | } 394 | 395 | // Range 396 | // Loop over a slice 397 | var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} 398 | 399 | // i - index 400 | // v - copy of the element in the array 401 | for i, v := range pow { 402 | fmt.Printf("2**%d = %d\n", i, v) 403 | } 404 | // Output: 405 | // 2**0 = 1 406 | // ... omitted 407 | // 2**7 = 128 408 | 409 | // If you only want the index: 410 | i := range pow 411 | 412 | // If you only want the value: 413 | _, v := range pow 414 | ``` 415 | 416 | ### If statment 417 | 418 | ```go 419 | if x < 0 { 420 | // Do something 421 | } 422 | 423 | if x < 0 { 424 | // Do x < 0 stuff 425 | } else { 426 | // Do x >= 0 stuff 427 | } 428 | 429 | if x < 0 { 430 | // Do x < 0 stuff 431 | } else if x == 0 { 432 | // Do x == 0 stuff 433 | } else { 434 | // Do x > 0 stuff 435 | } 436 | ``` 437 | 438 | ### Switch 439 | 440 | ```go 441 | // Switch does top down evaluation, stopping when case succeeds 442 | switch os := runtime.GOOS; os { 443 | case "darwin": 444 | fmt.Println("OS X.") 445 | case "linux": 446 | fmt.Println("Linux.") 447 | default: 448 | // freebsd, openbsd, 449 | // plan9, windows... 450 | fmt.Printf("%s.\n", os) 451 | } 452 | ``` 453 | 454 | ### defer 455 | 456 | - A defer statement defers the execution of a function until the surrounding function returns. 457 | 458 | ```go 459 | func main() { 460 | defer fmt.Println("world") 461 | fmt.Println("hello") 462 | } 463 | 464 | // Output: 465 | // hello 466 | // world 467 | ``` 468 | 469 | - defers can be stacked 470 | 471 | ```go 472 | func main() { 473 | fmt.Println("counting") 474 | for i := 0; i < 3; i++ { 475 | defer fmt.Println(i) 476 | } 477 | fmt.Println("done") 478 | } 479 | 480 | // Output: 481 | // counting 482 | // done 483 | // 2 484 | // 1 485 | // 0 486 | ``` 487 | 488 | ### Panic and Recover 489 | 490 | - Panic is used to create a run time error 491 | - Handle run time panics with the built-in recover function 492 | 493 | ```go 494 | func main() { 495 | defer func() { 496 | str := recover() 497 | fmt.Println(str) 498 | }() 499 | panic("PANIC") 500 | } 501 | 502 | // Output: 503 | // PANIC 504 | ``` 505 | 506 | ## Functions (in depth) 507 | 508 | ### Simple Function 509 | 510 | ```go 511 | func main(){ 512 | fmt.Println("Hello World") 513 | } 514 | ``` 515 | 516 | ### Function Arguments, returns 517 | 518 | ```go 519 | func add(x, y int) int { 520 | return x + y 521 | } 522 | ``` 523 | 524 | ### Named Returns 525 | 526 | Don't use this unless it's dead simple. It worsens readability 527 | 528 | ```go 529 | func f2() (r int) { 530 | r = 1 531 | return 532 | } 533 | ``` 534 | 535 | ### Return multiple values 536 | 537 | ```go 538 | func f() (int, int) { 539 | return 5, 6 540 | } 541 | ``` 542 | 543 | ### Variadic functions 544 | 545 | Used to pass an indeterminante number of args 546 | 547 | ```go 548 | func add(args ...int) int { 549 | total := 0 550 | for _, v := range args { 551 | total += v 552 | } 553 | return total 554 | } 555 | 556 | // Note: This is how fmt.Println is implemented: 557 | func Println(a ...interface{}) (n int, err error) 558 | ``` 559 | 560 | ### Methods on a type 561 | 562 | Note: Methods are functions 563 | 564 | ```go 565 | type Vertex struct { 566 | X, Y float64 567 | } 568 | 569 | func (v Vertex) Abs() float64 { 570 | return math.Sqrt(v.X*v.X + v.Y*v.Y) 571 | } 572 | ``` 573 | 574 | ### Method using a pointer reciever 575 | 576 | There are two reasons to use a pointer reciever 577 | 578 | 1. The first is so that the method can modify the value that its receiver points to. 579 | 1. The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example. 580 | 581 | ```go 582 | type Vertex struct { 583 | X, Y float64 584 | } 585 | 586 | func (v *Vertex) Scale(f float64) { 587 | v.X = v.X * f 588 | v.Y = v.Y * f 589 | } 590 | 591 | func (v *Vertex) Abs() float64 { 592 | return math.Sqrt(v.X*v.X + v.Y*v.Y) 593 | } 594 | 595 | func main() { 596 | v := &Vertex{3, 4} 597 | fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs()) // Before scaling: &{X:3 Y:4}, Abs: 5 598 | v.Scale(5) 599 | fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs()) //After scaling: &{X:15 Y:20}, Abs: 25 600 | } 601 | ``` 602 | 603 | ### Closure 604 | 605 | ```go 606 | func main() { 607 | add := func(x, y int) int { 608 | return x + y 609 | } 610 | fmt.Println(add(1,1)) //2 611 | } 612 | ``` 613 | 614 | ```go 615 | func main() { 616 | x := 0 617 | increment := func() { 618 | x++ 619 | } 620 | increment() 621 | increment() 622 | fmt.Println(x) //2 623 | } 624 | ``` 625 | 626 | ### Functions as return types 627 | 628 | ```go 629 | func makeHelloFunc() func() { 630 | return func() { 631 | fmt.Println("Hello") 632 | } 633 | } 634 | 635 | func main() { 636 | x := makeHelloFunc() 637 | x() 638 | } 639 | 640 | // Output: 641 | // Hello 642 | ``` 643 | 644 | ### Recursion 645 | 646 | Functions can call themselves 647 | 648 | ```go 649 | func factorial(x uint) uint { 650 | if x == 0 { 651 | return 1 652 | } 653 | return x * factorial(x-1) 654 | } 655 | ``` 656 | 657 | ## Goroutines 658 | 659 | - A goroutine is a lightweight thread managed by the Go runtime. 660 | ```go 661 | go f(x, y, z) // starts a new go routine 662 | ``` 663 | - Evaluation of f, x, y, z happen in the current goroutine and the execution of f happens in the new goroutine. 664 | 665 | ### Go Keyword 666 | 667 | - Any function can become a go routine just by using the `go` keyword 668 | - You can kind of think of `go` as Go's async. We'll look at some examples. 669 | 670 | ### Channels 671 | 672 | - Channels are a typed conduit through which you can send and receive values with the channel operator, <-. They are useful for syncing and allowing different goroutines to communicate. 673 | - By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables. 674 | 675 | ```go 676 | ch := make(chan int) // Make a channel of type int 677 | ch <- v // Send v to channel ch. 678 | v := <-ch // Receive from ch, and assign value to v. 679 | ``` 680 | 681 | ### Buffered Channels 682 | 683 | - Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel 684 | - Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty. 685 | 686 | ```go 687 | ch := make(chan int, 100) 688 | ``` 689 | 690 | ### Range and Close 691 | 692 | - Senders can close (`close(c)`) a channel to indicate that no more values will be sent 693 | - Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: 694 | 695 | ```go 696 | v, ok := <-ch 697 | // ok is false if there are no more values to receive and the channel is closed. 698 | ``` 699 | 700 | - The loop for i := range c receives values from the channel repeatedly until it is closed. 701 | 702 | ```go 703 | for i := range c { 704 | fmt.Println(i) 705 | } 706 | ``` 707 | 708 | ### Select 709 | 710 | - The select statement lets a goroutine wait on multiple communication operations. 711 | - The default case in a select is run if no other case is ready. 712 | 713 | ```go 714 | func fibonacci(c, quit chan int) { 715 | x, y := 0, 1 716 | for { 717 | select { 718 | case c <- x: 719 | x, y = y, x+y 720 | case <-quit: 721 | fmt.Println("quit") 722 | return 723 | default: 724 | // Runs if no other case is ready 725 | } 726 | } 727 | } 728 | 729 | func main() { 730 | c := make(chan int) 731 | quit := make(chan int) 732 | go func() { 733 | for i := 0; i < 10; i++ { 734 | fmt.Println(<-c) 735 | } 736 | quit <- 0 737 | }() 738 | fibonacci(c, quit) 739 | } 740 | ``` 741 | 742 | ### Directional Channels 743 | 744 | - Chanels can be send only and recieve only 745 | - `chan<- T` send only 746 | - `<-chan T` recieve only 747 | - If you can't remember which is which, look at the arrow. 748 | - Pointing into `chan` - send 749 | - Pointing away from `chan` - recieve 750 | - Useful for when you want to be sure that a function can only read or write (not both) to a channel 751 | 752 | ```go 753 | func SendOnly(pings chan<- string) { 754 | // pings is a send only chan 755 | } 756 | 757 | func RecieveOnly(pongs <-chan string) { 758 | // pongs is a recieve only chan 759 | } 760 | ``` 761 | --------------------------------------------------------------------------------