├── .gitignore ├── README.md ├── a-taste-of-go ├── idents.go └── point.go ├── a-tour-of-go ├── bin │ └── .gitkeep ├── pkg │ └── .gitkeep └── src │ ├── .gitkeep │ ├── basics │ └── Basics_test.go │ ├── concurrencies │ ├── EquivalentBinaryTrees_test.go │ └── Goroutines_test.go │ ├── interfaces │ ├── Errors_test.go │ ├── IPAddr_test.go │ ├── Interfaces_test.go │ ├── ReaderExercise_test.go │ └── Readers_test.go │ ├── methods │ └── MethodsAndInterfaces_test.go │ └── types │ └── MoreTypes_test.go ├── channel └── curious-channels.go ├── concurrency-is-not-parallelism └── load-balancer.go ├── concurrency-patterns ├── 00_basic.go ├── 01_generator.go ├── 02_fan_in.go ├── 03_restoring_sequence.go ├── 04_select_timeout.go ├── 05_select_all_timeout.go ├── 06_quit_channel.go ├── 07_quit_channel_cleanup.go ├── 08_daisy_chain.go ├── 11_google_search_v1.go ├── 12_google_search_v2.go ├── 13_google_search_v2.1.go ├── 14_google_search_v3.go └── glide.yaml ├── defer-panic-recover └── defer-panic-recover.go ├── errors └── error-handling-and-go.go ├── go-for-java-programmers └── google-search-frontend.go ├── go-kit-tutorial ├── stringsvc │ ├── .gitignore │ ├── Main.go │ ├── endpoint │ │ └── StringServiceEndpoint.go │ ├── glide.lock │ ├── glide.yaml │ ├── service │ │ └── StringService.go │ └── transport │ │ └── StringServiceTransport.go ├── stringsvc2 │ ├── .gitignore │ ├── Server.go │ ├── endpoint │ │ └── StringServiceEndpoint.go │ ├── glide.lock │ ├── glide.yaml │ ├── service │ │ ├── InstrumentMiddleware.go │ │ ├── Logging.go │ │ └── StringService.go │ └── transport │ │ └── StringServiceTransport.go └── stringsvc3 │ ├── .gitignore │ ├── Server.go │ ├── glide.lock │ ├── glide.yaml │ └── service │ ├── Endpoint.go │ ├── Instrument.go │ ├── Logging.go │ ├── Proxy.go │ ├── Service.go │ └── Transport.go ├── go-tooling-in-action ├── email-server.go └── email-server_test.go ├── golang-cheat-sheet ├── closure.go ├── if-statement.go ├── map-usage.go └── switch-statement.go ├── interface └── how-to-use-interfaces-in-go.go ├── json ├── json-interfaces-and-go-generate.go └── roman-numerals.go ├── just-for-func ├── 027_two-ways-of-merging-N-channels │ ├── main.go │ └── main_test.go └── README.md ├── map └── map-in-action.go ├── pointer └── things-i-wish-someone-had-told-me-about-go.go ├── slice └── slice-usage-and-internals_test.go └── understanding-nil ├── make-zero-value-useful.go └── understanding-nil.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | 4 | 5 | # Created by https://www.gitignore.io/api/go,emacs,intellij 6 | 7 | ### Go ### 8 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 9 | *.o 10 | *.a 11 | *.so 12 | 13 | # Folders 14 | _obj 15 | _test 16 | 17 | # Architecture specific extensions/prefixes 18 | *.[568vq] 19 | [568vq].out 20 | 21 | *.cgo1.go 22 | *.cgo2.c 23 | _cgo_defun.c 24 | _cgo_gotypes.go 25 | _cgo_export.* 26 | 27 | _testmain.go 28 | 29 | *.exe 30 | *.test 31 | *.prof 32 | 33 | 34 | ### Emacs ### 35 | # -*- mode: gitignore; -*- 36 | *~ 37 | \#*\# 38 | /.emacs.desktop 39 | /.emacs.desktop.lock 40 | *.elc 41 | auto-save-list 42 | tramp 43 | .\#* 44 | 45 | # Org-mode 46 | .org-id-locations 47 | *_archive 48 | 49 | # flymake-mode 50 | *_flymake.* 51 | 52 | # eshell files 53 | /eshell/history 54 | /eshell/lastdir 55 | 56 | # elpa packages 57 | /elpa/ 58 | 59 | # reftex files 60 | *.rel 61 | 62 | # AUCTeX auto folder 63 | /auto/ 64 | 65 | # cask packages 66 | .cask/ 67 | dist/ 68 | 69 | # Flycheck 70 | flycheck_*.el 71 | 72 | # server auth directory 73 | /server/ 74 | 75 | # projectiles files 76 | .projectile 77 | 78 | ### Intellij ### 79 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 80 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 81 | 82 | # User-specific stuff: 83 | .idea/workspace.xml 84 | .idea/tasks.xml 85 | .idea/dictionaries 86 | .idea/vcs.xml 87 | .idea/jsLibraryMappings.xml 88 | 89 | # Sensitive or high-churn files: 90 | .idea/dataSources.ids 91 | .idea/dataSources.xml 92 | .idea/dataSources.local.xml 93 | .idea/sqlDataSources.xml 94 | .idea/dynamic.xml 95 | .idea/uiDesigner.xml 96 | 97 | # Gradle: 98 | .idea/gradle.xml 99 | .idea/libraries 100 | 101 | # Mongo Explorer plugin: 102 | .idea/mongoSettings.xml 103 | 104 | ## File-based project format: 105 | *.iws 106 | 107 | ## Plugin-specific files: 108 | 109 | # IntelliJ 110 | /out/ 111 | 112 | # mpeltonen/sbt-idea plugin 113 | .idea_modules/ 114 | 115 | # JIRA plugin 116 | atlassian-ide-plugin.xml 117 | 118 | # Crashlytics plugin (for Android Studio and IntelliJ) 119 | com_crashlytics_export_strings.xml 120 | crashlytics.properties 121 | crashlytics-build.properties 122 | fabric.properties 123 | 124 | ### Intellij Patch ### 125 | *.iml 126 | 127 | !.gitkeep 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang 2 | 3 | maintains *MUST READ* articles 4 | 5 | ## Quick Tutorial 6 | 7 | - [A Taste of Go](https://talks.golang.org/2014/taste.slide#40) 8 | - [Golang Cheat Sheet](https://github.com/a8m/go-lang-cheat-sheet) 9 | - [Go for Java Programmers](https://talks.golang.org/2015/go-for-java-programmers.slide) 10 | - [Golang Naming Convention](https://talks.golang.org/2014/names.slide) 11 | 12 | ## Language Design 13 | 14 | > When reading code, it should be clear what the program will do. 15 | > When writing code, it should be clear how to make the program do what you want. 16 | - [Rob Pike: Simplicity is Complicated](https://www.youtube.com/watch?v=rFejpH_tAHM) 17 | 18 | > Leading edge language features don't usually address what you really want. 19 | > Golang is designed for large applications, large teams and large dependencies. 20 | - **Rob Pike: Go at Google** [Article](https://talks.golang.org/2012/splash.article), [Video](https://www.infoq.com/presentations/Go-Google) 21 | 22 | - [Golang FAQ: Design](https://golang.org/doc/faq#Design) 23 | - [Golang FAQ: Types](https://golang.org/doc/faq#types) 24 | - [Campoy: Functional Go?](https://www.youtube.com/watch?v=ouyHp2nJl0I) 25 | 26 | ## Tools 27 | 28 | - [Go Tooling in Action](https://www.youtube.com/watch?v=uBjoTxosSys) 29 | - **Debugging Go programs with Delve** [Article](https://blog.gopheracademy.com/advent-2015/debugging-with-delve/), [Video](https://www.youtube.com/watch?v=InG72scKPd4) 30 | 31 | ## Idiomatic Go 32 | 33 | - **Rob Pike: Go Proverb** [Page](https://go-proverbs.github.io/), [Video](https://www.youtube.com/watch?v=PAAkCSZUG1c) 34 | - [Campoy: Understanding nil](https://www.youtube.com/watch?v=ynoY2xz-F8s) 35 | - **Twelve Go Best Practices**: [Slide](https://talks.golang.org/2013/bestpractices.slide#1), [Video](https://www.youtube.com/watch?v=8D3Vmm1BGoY) 36 | - **Go best practices, six years in:** [Article](https://peter.bourgon.org/go-best-practices-2016/), [Video](https://www.infoq.com/presentations/go-patterns) 37 | 38 | ## Concurrency 39 | 40 | > *Concurrency* is about **structure**, while *Paralleism* is about **execution** 41 | 42 | - **Rob Pike: Concurrency is not Parallelism** [Slide](https://talks.golang.org/2012/waza.slide), [Video](https://www.youtube.com/watch?v=B9lP-E4J_lc) 43 | - [Go Concurrency Patterns](https://www.youtube.com/watch?v=f6kdp27TYZs) 44 | - [Curious Channels](https://dave.cheney.net/2013/04/30/curious-channels) 45 | - [Complex Concurrency Patterns With Go](https://www.youtube.com/watch?v=2HOO5gIgyMg) 46 | - [Advanced Go Concurrency Patterns](https://www.youtube.com/watch?v=QDDwwePbDtw) 47 | 48 | ## Error Handling 49 | 50 | > Error values in Go aren’t special, they are just values like any other, and so you have the entire language at your disposal. 51 | 52 | - [Go Blog: Defer, Panic, and Recover](https://blog.golang.org/defer-panic-and-recover) 53 | - [Go Blog: Error Handling and Go](https://blog.golang.org/error-handling-and-go) 54 | - [Go Blog: Errors are Values](https://blog.golang.org/errors-are-values) 55 | - **Dave Cheney: Don't just check errors, handle them gracefully** [Article](https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully), [Video](https://www.youtube.com/watch?v=lsBF58Q-DnY) 56 | 57 | > Go solves the exception problem by not having exceptions. 58 | > ... 59 | > The decision to not include exceptions in Go is an example of its simplicity and orthogonality. Using multiple return values and a simple convention, Go solves the problem of letting programmers know when things have gone wrong and reserves panic for the truly exceptional. 60 | 61 | - [Dave Cheney: Why Go gets exceptions right](https://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right) 62 | - [Dave Cheney: Inspecting errors](https://dave.cheney.net/2014/12/24/inspecting-errors) 63 | - [Dave Cheney: Error handling vs. exceptions redux](https://dave.cheney.net/2014/11/04/error-handling-vs-exceptions-redux) 64 | - [Dave Cheney: Errors and Exceptions, redux](https://dave.cheney.net/2015/01/26/errors-and-exceptions-redux) 65 | 66 | > Knowing the difference between which errors to ignore and which to check is why we’re paid as professionals. 67 | 68 | - [Dave Cheney: Constant errors](https://dave.cheney.net/2016/04/07/constant-errors) 69 | - [Dave Cheney: Stack traces and the errors package](https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package) 70 | 71 | ## Interface 72 | 73 | > If C++ and Java are about type hierarchies and the taxonomy of types, **Go is about composition** 74 | 75 | > Q. Hey gophers, what was the best/worst moment of your experienes lenaring golang? 76 | > A. **The worst was interface, but the best was also interface** 77 | 78 | - [Stackoverflow: What's the mearning of interface{} ?](http://stackoverflow.com/questions/23148812/go-whats-the-meaning-of-interface) 79 | - [How to use interfaces in Go](http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go) 80 | 81 | ## Struct 82 | 83 | - [Dave Cheney: Struct composition with Go](https://dave.cheney.net/2015/05/22/struct-composition-with-go) 84 | - [Dave Cheney: The empty struct](https://dave.cheney.net/2014/03/25/the-empty-struct) 85 | 86 | ## Pointer 87 | 88 | - [Dave Cheney: Pointers in Go](https://dave.cheney.net/2014/03/17/pointers-in-go) 89 | - [Things I Wish Someone Had Told Me About Go](http://openmymind.net/Things-I-Wish-Someone-Had-Told-Me-About-Go/) 90 | - [Dave Cheney: Go has both make and new functions, what gives?](https://dave.cheney.net/2014/08/17/go-has-both-make-and-new-functions-what-gives) 91 | - [Dave Cheney: Should methods be declared on T or *T](https://dave.cheney.net/2016/03/19/should-methods-be-declared-on-t-or-t) 92 | 93 | ## Map, Slice 94 | 95 | - [Go Blog: Map in Action](https://blog.golang.org/go-maps-in-action) 96 | - [Go Blog: Slices Usage and Internals](https://blog.golang.org/go-slices-usage-and-internals) 97 | 98 | ## Logging 99 | 100 | - [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1) 101 | - [Logging v. instrumentation](https://peter.bourgon.org/blog/2016/02/07/logging-v-instrumentation.html) 102 | - [Dave Cheney: Let’s talk about logging](https://dave.cheney.net/2015/11/05/lets-talk-about-logging) 103 | 104 | ## Encoding, JSON 105 | 106 | - [JSON, interface, and go generate](https://www.youtube.com/watch?v=YgnD27GFcyA) 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /a-taste-of-go/idents.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "text/scanner" 7 | ) 8 | 9 | func main() { 10 | var s scanner.Scanner 11 | s.Init(os.Stdin) 12 | 13 | for { 14 | switch s.Scan() { 15 | case scanner.EOF: 16 | return // all done 17 | case scanner.Ident: 18 | fmt.Println(s.TokenText()) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /a-taste-of-go/point.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Point struct { 8 | x, y int 9 | } 10 | 11 | func (p Point) String() string { 12 | return fmt.Sprintf("(%d, %d)", p.x, p.y) 13 | } 14 | 15 | type Stringer interface { 16 | String() string 17 | } 18 | 19 | func main() { 20 | p := Point{2, 3} 21 | var x Stringer = p 22 | // Method calls via interface types are dynamically dispatched (virtual function call) 23 | fmt.Println(x) 24 | // Method calls via non-interface types are statically dispatched 25 | fmt.Println(Point{3, 5}) // fmt.Println knows about Stringer 26 | } 27 | -------------------------------------------------------------------------------- /a-tour-of-go/bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ambda/golang/d6a4724124ca05493a27017f8bf71a948ece6be7/a-tour-of-go/bin/.gitkeep -------------------------------------------------------------------------------- /a-tour-of-go/pkg/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ambda/golang/d6a4724124ca05493a27017f8bf71a948ece6be7/a-tour-of-go/pkg/.gitkeep -------------------------------------------------------------------------------- /a-tour-of-go/src/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ambda/golang/d6a4724124ca05493a27017f8bf71a948ece6be7/a-tour-of-go/src/.gitkeep -------------------------------------------------------------------------------- /a-tour-of-go/src/basics/Basics_test.go: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/cmplx" 7 | "math/rand" 8 | "runtime" 9 | ) 10 | 11 | func f1() { 12 | const Pi = 3.14 13 | } 14 | 15 | func f2() { 16 | fmt.Println("My favorite number is", rand.Intn(10)) 17 | } 18 | 19 | func f3() { 20 | fmt.Printf("Now you have %g problems", math.Sqrt(7)) 21 | } 22 | 23 | func add(x, y int) int { 24 | return x + y 25 | } 26 | 27 | func swap(s1, s2 string) (string, string) { 28 | return s2, s1 29 | } 30 | 31 | func split(sum int) (x, y int) { 32 | x = sum * 4 / 9 33 | y = sum - x 34 | return 35 | } 36 | 37 | func ExampleFormatting1() { 38 | var ( 39 | ToBe bool = false 40 | MaxInt uint64 = (1 << 64) - 1 41 | z complex128 = cmplx.Sqrt(-5 + 12i) 42 | ) 43 | 44 | const f = "type: %T, value: %v\n" 45 | 46 | println(ToBe, MaxInt, z) 47 | 48 | fmt.Printf(f, ToBe, ToBe) 49 | fmt.Printf(f, MaxInt, MaxInt) 50 | fmt.Printf(f, z, z) 51 | 52 | // Output: 53 | // type: bool, value: false 54 | // type: uint64, value: 18446744073709551615 55 | // type: complex128, value: (2+3i) 56 | } 57 | 58 | func ExampleFormatting2() { 59 | var i int 60 | var f float64 61 | var b bool 62 | var s string 63 | 64 | fmt.Printf("%v %v %v %q\n", i, f, b, s) 65 | 66 | // Output: 0 0 false "" 67 | } 68 | 69 | func ExampleCastType() { 70 | i := 42 71 | f := float64(i) 72 | u := uint(f) 73 | 74 | fmt.Printf("%v %v %v \n", i, f, u) 75 | 76 | // Output: 42 42 42 77 | } 78 | 79 | func sum(xs []int) int { 80 | acc := 0 81 | 82 | for _, v := range xs { 83 | acc += v 84 | } 85 | 86 | return acc 87 | } 88 | 89 | func ExampleWhileUsingFor() { 90 | acc := 1 91 | 92 | for acc < 1000 { 93 | acc += 1 94 | } 95 | 96 | fmt.Println(acc) 97 | 98 | // Output: 1000 99 | } 100 | 101 | func sqrt(x float64) string { 102 | if x < 0 { 103 | return sqrt(-x) + "i" 104 | } 105 | 106 | return fmt.Sprint(math.Sqrt(x)) 107 | } 108 | 109 | func pow1(x, n, lim float64) float64 { 110 | if v := math.Pow(x, n); v < lim { 111 | return v 112 | } 113 | 114 | return lim 115 | } 116 | 117 | func pow2(x, n, lim float64) float64 { 118 | if v := math.Pow(x, n); v < lim { 119 | return v 120 | } else { 121 | fmt.Printf("%g >= %g\n", v, lim) 122 | } 123 | 124 | return lim // can't use v here 125 | } 126 | 127 | func Sqrt1(x float64) float64 { 128 | z := float64(1) 129 | 130 | for i := 1; i < 10; i++ { 131 | z = z - (math.Pow(z, 2)-x)/(2*z) 132 | } 133 | 134 | return z 135 | } 136 | 137 | func printOS() { 138 | switch os := runtime.GOOS; os { 139 | case "darwin": 140 | fmt.Println("OSX!") 141 | case "linux": 142 | fmt.Println("LINUX!") 143 | default: 144 | fmt.Printf("Maybe Window, %s", os) 145 | } 146 | } 147 | 148 | func deferTest() { 149 | // deferred call's ars are evaluated immediately 150 | // ut function call is not execued until the surrouding function returns 151 | defer fmt.Println("world") 152 | 153 | fmt.Println("hello") 154 | } 155 | 156 | func ExampleDeferStack() { 157 | // deferred calls are pushed onto a stack 158 | // when a function returnes, its deferred calls are executed in LIFO order 159 | 160 | for i := 0; i < 10; i++ { 161 | defer fmt.Println(i) 162 | } 163 | 164 | fmt.Println("return") 165 | 166 | // Output: 167 | // return 168 | // 9 169 | // 8 170 | // 7 171 | // 6 172 | // 5 173 | // 4 174 | // 3 175 | // 2 176 | // 1 177 | // 0 178 | } 179 | -------------------------------------------------------------------------------- /a-tour-of-go/src/concurrencies/EquivalentBinaryTrees_test.go: -------------------------------------------------------------------------------- 1 | package concurrencies 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/tour/tree" 6 | ) 7 | 8 | /* 9 | type Tree struct { 10 | Left *Tree 11 | Value int 12 | Right *Tree 13 | } 14 | 15 | tree.New(k) constructs randomly-structured binary tree holding the values k, ..., 10k 16 | */ 17 | 18 | func _walk(t *tree.Tree, ch chan int) { 19 | if t != nil { // in-order 20 | _walk(t.Left, ch) 21 | ch <- t.Value 22 | _walk(t.Right, ch) 23 | } 24 | } 25 | 26 | func Walk(t *tree.Tree, ch chan int) { 27 | _walk(t, ch) 28 | close(ch) 29 | } 30 | 31 | func ExampleWalk1() { 32 | ch := make(chan int) 33 | 34 | go Walk(tree.New(1), ch) 35 | 36 | for v := range ch { 37 | fmt.Println(v) 38 | } 39 | 40 | // Output: 41 | // 1 42 | // 2 43 | // 3 44 | // 4 45 | // 5 46 | // 6 47 | // 7 48 | // 8 49 | // 9 50 | // 10 51 | } 52 | 53 | func Same(t1, t2 *tree.Tree) bool { 54 | ch1, ch2 := make(chan int), make(chan int) 55 | 56 | go Walk(t1, ch1) 57 | go Walk(t2, ch2) 58 | 59 | for { 60 | v1, ok1 := <-ch1 61 | v2, ok2 := <-ch2 62 | 63 | if (!ok1 || !ok2) || (v1 != v2) { 64 | return false 65 | } 66 | 67 | if ok1 && ok2 { 68 | return true 69 | } 70 | } 71 | } 72 | 73 | func ExampleSame1() { 74 | b1 := Same(tree.New(1), tree.New(1)) 75 | b2 := Same(tree.New(1), tree.New(2)) 76 | b3 := Same(tree.New(5), tree.New(3)) 77 | 78 | fmt.Println(b1) 79 | fmt.Println(b2) 80 | fmt.Println(b3) 81 | 82 | // Output: 83 | // true 84 | // false 85 | // false 86 | } 87 | -------------------------------------------------------------------------------- /a-tour-of-go/src/concurrencies/Goroutines_test.go: -------------------------------------------------------------------------------- 1 | package concurrencies 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /* 9 | 10 | A `goroutine` is a lightweight thread managed by the Go runtime 11 | 12 | The evaluation of `f`, `x`, `y` and `z` happens in the current goroutine and 13 | the execution of `f` happens in the new goroutine 14 | 15 | Goroutines run in the same address space, so access to shared memory must be synchronized 16 | (The `sync` package provides useful primitives) 17 | 18 | */ 19 | 20 | func say(s string) { 21 | for i := 0; i < 5; i++ { 22 | time.Sleep(100 * time.Millisecond) 23 | fmt.Println(s) 24 | } 25 | } 26 | 27 | func ExampleGoroutinesSay() { 28 | go say("world") 29 | say("hello") 30 | 31 | // Output might be 32 | // hello 33 | // world 34 | // world 35 | // hello 36 | // hello 37 | // world 38 | // world 39 | // hello 40 | // hello 41 | // world 42 | } 43 | 44 | /* 45 | 46 | Channels are a typed conduit through which you can send and receive values 47 | By default, sends are receives block until the other side is ready. 48 | 49 | */ 50 | 51 | func sum(s []int, c chan int) { 52 | sum := 0 53 | 54 | for _, v := range s { 55 | sum += v 56 | } 57 | 58 | c <- sum // send sum to c 59 | } 60 | 61 | func ExampleChannels() { 62 | s := []int{7, 2, 8, -9, 4, 0} 63 | c := make(chan int) 64 | 65 | left := s[:len(s)/2] 66 | right := s[len(s)/2:] 67 | 68 | go sum(left, c) 69 | go sum(right, c) 70 | 71 | x, y := <-c, <-c 72 | 73 | fmt.Println(x + y) 74 | 75 | // Output: 12 76 | } 77 | 78 | func ExampleBufferedChannels() { 79 | // Send to a buffered channel block only when the buffer is full. 80 | // Receives block when the buffer is empty 81 | ch := make(chan int, 2) 82 | 83 | ch <- 1 84 | ch <- 2 85 | 86 | fmt.Println(<-ch) 87 | fmt.Println(<-ch) 88 | 89 | // Output: 90 | // 1 91 | // 2 92 | } 93 | 94 | func fibo1(n int, c chan int) { 95 | x, y := 0, 1 96 | 97 | for i := 0; i < n; i++ { 98 | c <- x 99 | x, y = y, x+y 100 | } 101 | 102 | close(c) 103 | } 104 | 105 | func ExampleRangeAndClose() { 106 | c := make(chan int, 10) 107 | 108 | fmt.Println(cap(c)) 109 | 110 | go fibo1(cap(c), c) 111 | 112 | // we can test whether a channel is closed or not by `v, ok := <- c` 113 | 114 | // range loop receives values from the channel repeatedly until it is closed 115 | for i := range c { 116 | fmt.Println(i) 117 | } 118 | 119 | /* 120 | Only the sender should close a channel, never the receiver. 121 | Sending on a closed channel will cause a panic 122 | 123 | Channels aren't like files. You don't usually need to close them. 124 | Closing is only necessary when the receiver must be told there are no more values coming, 125 | such as to terminate a `range` loop 126 | */ 127 | 128 | // Output: 129 | // 10 130 | // 0 131 | // 1 132 | // 1 133 | // 2 134 | // 3 135 | // 5 136 | // 8 137 | // 13 138 | // 21 139 | // 34 140 | } 141 | 142 | /* 143 | The `select` statement lets a goroutine wait on multiple communication operations 144 | A `select` blocks until one of its cases can run, then it executes that case. 145 | It chooses one at random if multiple are ready. 146 | */ 147 | 148 | func fibo2(c, quit chan int) { 149 | x, y := 0, 1 150 | for { 151 | select { 152 | case c <- x: 153 | x, y = y, x+y 154 | case <-quit: 155 | fmt.Println("quit") 156 | return 157 | } 158 | } 159 | } 160 | 161 | func ExampleSelect() { 162 | c := make(chan int) 163 | quit := make(chan int) 164 | 165 | go func() { 166 | for i := 0; i < 10; i++ { 167 | fmt.Println(<-c) 168 | } 169 | quit <- 0 170 | }() 171 | 172 | fibo2(c, quit) 173 | 174 | // Output: 175 | // 0 176 | // 1 177 | // 1 178 | // 2 179 | // 3 180 | // 5 181 | // 8 182 | // 13 183 | // 21 184 | // 34 185 | // quit 186 | } 187 | 188 | func ExampleDefaultSelection() { 189 | tick := time.Tick(100 * time.Millisecond) 190 | boom := time.After(500 * time.Millisecond) 191 | 192 | for { 193 | select { 194 | case <-tick: 195 | fmt.Println("tick") 196 | case <-boom: 197 | fmt.Println("boom!") 198 | return 199 | default: 200 | fmt.Println(".") 201 | time.Sleep(50 * time.Millisecond) 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /a-tour-of-go/src/interfaces/Errors_test.go: -------------------------------------------------------------------------------- 1 | package interfaces 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /* 9 | 10 | The error type is a built-in interface similar to fmt.Stringer 11 | 12 | type error interface { 13 | Error() string 14 | } 15 | 16 | */ 17 | 18 | type MyError struct { 19 | When time.Time 20 | What string 21 | } 22 | 23 | func (e *MyError) Error() string { 24 | return fmt.Sprintf("at %v, %s", e.When, e.What) 25 | } 26 | 27 | func raiseMyError() error { 28 | return &MyError{ 29 | time.Now(), 30 | "it didn't work", 31 | } 32 | } 33 | 34 | func ExampleMyError() { 35 | if err := raiseMyError(); err != nil { 36 | fmt.Println(err) 37 | } 38 | 39 | // Output looks like `at 2016-05-10 00:27:03.91289924 +0900 KST, it didn't work` 40 | } 41 | -------------------------------------------------------------------------------- /a-tour-of-go/src/interfaces/IPAddr_test.go: -------------------------------------------------------------------------------- 1 | package interfaces 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type IPAddr [4]byte 8 | 9 | func (ip IPAddr) String() string { 10 | return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) 11 | } 12 | 13 | func ExampleIPAddrStringer() { 14 | addrs := map[string]IPAddr{ 15 | "loopback": {127, 0, 0, 1}, 16 | "googleDNS": {8, 8, 8, 8}, 17 | } 18 | 19 | for name, addr := range addrs { 20 | fmt.Printf("%v: %v\n", name, addr) 21 | } 22 | 23 | // Output might be 24 | // loopback: 127.0.0.1 25 | // googleDNS: 8.8.8.8 26 | } 27 | -------------------------------------------------------------------------------- /a-tour-of-go/src/interfaces/Interfaces_test.go: -------------------------------------------------------------------------------- 1 | package interfaces 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type MyFloat float64 9 | type Vertex struct { 10 | X, Y float64 11 | } 12 | 13 | type Abser interface { 14 | Abs() float64 15 | } 16 | 17 | func (f MyFloat) Abs() float64 { 18 | if f < 0 { 19 | return float64(-f) 20 | } 21 | 22 | return float64(f) 23 | } 24 | 25 | func (v *Vertex) Abs() float64 { 26 | return math.Sqrt(v.X*v.X + v.Y*v.Y) 27 | } 28 | 29 | func ExampleInterfaces() { 30 | var a Abser 31 | 32 | f := MyFloat(-math.Sqrt2) 33 | v := Vertex{3, 4} 34 | 35 | a = f // MyFloat implements Abser 36 | fmt.Println(a) 37 | a = &v // *Vertex implements Abser (not Vertex4) 38 | fmt.Println(a) 39 | 40 | // Output: 41 | // -1.4142135623730951 42 | // &{3 4} 43 | } 44 | 45 | type I interface { 46 | M() 47 | } 48 | 49 | type T struct { 50 | S string 51 | } 52 | 53 | func (t *T) M() { 54 | if t == nil { 55 | fmt.Println("") 56 | return 57 | } 58 | 59 | fmt.Println(t.S) 60 | } 61 | 62 | func describe(i I) { 63 | fmt.Printf("(%v, %T)\n", i, i) 64 | } 65 | 66 | func ExampleInterfacesValue() { 67 | var i I 68 | var t *T 69 | 70 | i = t // method `M` will be called with a nil receiver 71 | describe(i) // An interface value that holds a nil concrete value is itself non-nil 72 | fmt.Println(i == nil) 73 | i.M() 74 | 75 | i = &T{"hello"} 76 | describe(i) 77 | fmt.Println(i == nil) 78 | i.M() 79 | 80 | // Output: 81 | // (, *interfaces.T) 82 | // false 83 | // 84 | // (&{hello}, *interfaces.T) 85 | // false 86 | // hello 87 | } 88 | 89 | func ExampleNilInterfaceValues() { 90 | var i I 91 | describe(i) // A nil interface value holds niether value nor concrete type 92 | fmt.Println(i == nil) 93 | 94 | // i.M() will cause runtime error 95 | 96 | // Output: 97 | // (, ) 98 | // true 99 | } 100 | 101 | func describeAny(i interface{}) { 102 | fmt.Printf("(%v, %T)\n", i, i) 103 | } 104 | 105 | func ExampleEmptyInterface() { 106 | // An empty interface may hold values of any type 107 | // since every type implements ay least zero methods 108 | 109 | var i interface{} 110 | describeAny(i) 111 | 112 | i = 42 113 | describeAny(i) 114 | 115 | i = "hello" 116 | describeAny(i) 117 | 118 | // Output: 119 | // (, ) 120 | // (42, int) 121 | // (hello, string) 122 | } 123 | 124 | func ExampleTypeAssertions() { 125 | var i interface{} = "hello" 126 | 127 | s := i.(string) 128 | fmt.Println(s) 129 | 130 | s, ok := i.(string) 131 | fmt.Println(s, ok) 132 | 133 | f, ok := i.(float64) 134 | fmt.Println(f, ok) 135 | 136 | // f := i.float(64) will cause panic 137 | 138 | // Output: 139 | // hello 140 | // hello true 141 | // 0 false 142 | } 143 | 144 | func SwitchType(i interface{}) { 145 | switch v := i.(type) { 146 | case int: 147 | fmt.Printf("Twice %v is %v\n", v, v*2) 148 | case string: 149 | fmt.Printf("%q is %v bytes long\n", v, len(v)) 150 | default: 151 | // v is of the same interface type and value as i 152 | fmt.Printf("I don't know about type %T!\n", v) 153 | } 154 | } 155 | 156 | func ExampleTypeSwitches() { 157 | SwitchType(21) 158 | SwitchType("hello") 159 | SwitchType(true) 160 | 161 | // Output: 162 | // Twice 21 is 42 163 | // "hello" is 5 bytes long 164 | // I don't know about type bool! 165 | } 166 | -------------------------------------------------------------------------------- /a-tour-of-go/src/interfaces/ReaderExercise_test.go: -------------------------------------------------------------------------------- 1 | package interfaces 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type rot13Reader struct { 10 | r io.Reader 11 | } 12 | 13 | func rotate13(b byte) byte { 14 | if 'a' <= b && b <= 'z' { 15 | return (b-'a'+13)%26 + 'a' 16 | } 17 | 18 | if 'A' <= b && b <= 'Z' { 19 | return (b-'A'+13)%26 + 'A' 20 | } 21 | 22 | return b 23 | } 24 | 25 | func (r13 rot13Reader) Read(b []byte) (int, error) { 26 | n, err := r13.r.Read(b) 27 | 28 | for i := 0; i < n; i++ { 29 | b[i] = rotate13(b[i]) 30 | } 31 | 32 | return n, err 33 | } 34 | 35 | func ExampleR13Reader() { 36 | s := strings.NewReader("Lbh penpxrq gur pbqr!") 37 | r := rot13Reader{s} 38 | io.Copy(os.Stdout, &r) 39 | 40 | // Output: You cracked the code! 41 | } 42 | -------------------------------------------------------------------------------- /a-tour-of-go/src/interfaces/Readers_test.go: -------------------------------------------------------------------------------- 1 | package interfaces 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // Reader represents the read end of a stream of date 10 | func ExampleReaders() { 11 | r := strings.NewReader("Hello, Reader!") 12 | b := make([]byte, 8) 13 | 14 | // func (T) Read(b []byte) (n int, err error) 15 | for { 16 | n, err := r.Read(b) 17 | fmt.Printf("n = %v, err = %v, b = %v\n", n, err, b) 18 | 19 | // %q: a single-quoted char literal safely escaped with Go syntax 20 | // see https://golang.org/pkg/fmt/ 21 | fmt.Printf("b[:n] = %q\n", b[:n]) 22 | 23 | if err == io.EOF { 24 | break 25 | } 26 | } 27 | 28 | // Output: 29 | // n = 8, err = , b = [72 101 108 108 111 44 32 82] 30 | // b[:n] = "Hello, R" 31 | // n = 6, err = , b = [101 97 100 101 114 33 32 82] 32 | // b[:n] = "eader!" 33 | // n = 0, err = EOF, b = [101 97 100 101 114 33 32 82] 34 | // b[:n] = "" 35 | } 36 | -------------------------------------------------------------------------------- /a-tour-of-go/src/methods/MethodsAndInterfaces_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Vertex3 struct { 9 | X, Y float64 10 | } 11 | 12 | /** A method is a function with a special receiver argument */ 13 | func (v Vertex3) Abs() float64 { 14 | return math.Sqrt(v.X*v.X + v.Y*v.Y) 15 | } 16 | 17 | func Abs2(v Vertex3) float64 { 18 | return math.Sqrt(v.X*v.X + v.Y*v.Y) 19 | } 20 | 21 | func ExampleMethods() { 22 | v := Vertex3{3, 4} 23 | fmt.Println(v.Abs()) /** method */ 24 | fmt.Println(Abs2(v)) /** function */ 25 | 26 | // Output: 27 | // 5 28 | // 5 29 | } 30 | 31 | /** We can declare a method on non-struct types too */ 32 | type MyFloat0 float64 33 | 34 | func (f MyFloat0) Abs() float64 { 35 | if f < 0 { 36 | return float64(-f) 37 | } 38 | 39 | return float64(f) 40 | } 41 | 42 | func ExampleMethodContinued() { 43 | f := MyFloat0(-math.Sqrt2) 44 | 45 | fmt.Println(f.Abs()) 46 | 47 | // Output: 1.4142135623730951 48 | } 49 | 50 | func (v Vertex3) Scale1(f float64) { 51 | v.X = v.X * f 52 | v.Y = v.Y * f 53 | } 54 | 55 | func (v *Vertex3) Scale2(f float64) { 56 | v.X = v.X * f 57 | v.Y = v.Y * f 58 | } 59 | 60 | func ExamplePointerReceivers() { 61 | v := Vertex3{3, 4} 62 | v.Scale1(10) 63 | fmt.Println(v.Abs()) /** value receiver */ 64 | v.Scale2(10) 65 | fmt.Println(v.Abs()) /** pointer receiver */ 66 | 67 | // Output: 68 | // 5 69 | // 50 70 | } 71 | 72 | func Scale3(v Vertex3, f float64) { 73 | v.X = v.X * f 74 | v.Y = v.Y * f 75 | } 76 | 77 | func Scale4(v *Vertex3, f float64) { 78 | v.X = v.X * f 79 | v.Y = v.Y * f 80 | } 81 | 82 | func ExamplePointersAndFunctions() { 83 | v := Vertex3{3, 4} 84 | 85 | Scale3(v, 10) /** will copy v */ 86 | fmt.Println(v) 87 | 88 | Scale4(&v, 10) /** will use the original v */ 89 | fmt.Println(v) 90 | 91 | // Output: 92 | // {3 4} 93 | // {30 40} 94 | } 95 | 96 | func ExampleMethodAndPointerIndirection() { 97 | v1 := Vertex3{3, 4} 98 | /** Scale4(v1, 10) will cause compile error since Scale4 requires Vertex pointer */ 99 | 100 | v1.Scale2(10) 101 | fmt.Println(v1) /** receivers can take either a value or a pointer */ 102 | 103 | p1 := &Vertex3{4, 3} 104 | p1.Scale2(10) 105 | fmt.Println(*p1) 106 | p1.Scale1(10) /** method will copy p1 instead of using p1 */ 107 | fmt.Println(*p1) 108 | 109 | // Output: 110 | // {30 40} 111 | // {40 30} 112 | // {40 30} 113 | } 114 | -------------------------------------------------------------------------------- /a-tour-of-go/src/types/MoreTypes_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strings" 7 | ) 8 | 9 | func ExamplePointers1() { 10 | i, j := 42, 2701 11 | 12 | p := &i // address of i to p 13 | fmt.Println(*p) 14 | 15 | *p = 21 16 | fmt.Println(i) 17 | 18 | p = &j 19 | *p = *p / 37 20 | fmt.Println(j) 21 | 22 | // Output: 23 | // 42 24 | // 21 25 | // 73 26 | } 27 | 28 | type Vertex struct { 29 | X int 30 | Y int 31 | } 32 | 33 | func ExampleStruct1() { 34 | fmt.Println(Vertex{1, 2}) 35 | 36 | // Output: {1 2} 37 | } 38 | 39 | func ExampleStruct2() { 40 | v := Vertex{1, 2} 41 | v.X = 4 42 | 43 | fmt.Println(v.X) 44 | 45 | // Output: 4 46 | } 47 | 48 | func ExampleStructLiterals1() { 49 | var ( 50 | v1 = Vertex{1, 2} 51 | v2 = Vertex{X: 1} // Y: 0 is implicit 52 | v3 = Vertex{} // X: 0 and Y: 0 53 | p = &Vertex{1, 3} 54 | ) 55 | 56 | fmt.Println(v1) 57 | fmt.Println(v2) 58 | fmt.Println(v3) 59 | fmt.Println(p) 60 | 61 | // Output: 62 | // {1 2} 63 | // {1 0} 64 | // {0 0} 65 | // &{1 3} 66 | } 67 | 68 | func ExampleArrays1() { 69 | var a [2]string 70 | a[0] = "Hello" 71 | a[1] = "World" 72 | fmt.Println(a[0], a[1]) 73 | fmt.Println(a) 74 | 75 | primes := [6]int{2, 3, 5, 7, 11, 13} 76 | fmt.Println(primes) 77 | 78 | // Output: 79 | // Hello World 80 | // [Hello World] 81 | // [2 3 5 7 11 13] 82 | } 83 | 84 | func ExampleSlices1() { /** slice is a dynamically-sized */ 85 | primes := [6]int{2, 3, 5, 7, 11, 13} 86 | 87 | var s []int = primes[1:4] 88 | fmt.Println(s) 89 | 90 | // Output: [3 5 7] 91 | } 92 | 93 | func ExampleSlices2() { 94 | /** slice does not store any data, 95 | * it just describes a section of an underlying array 96 | */ 97 | 98 | names := [4]string{ 99 | "John", 100 | "Paul", 101 | "George", 102 | "Ringo", 103 | } 104 | 105 | fmt.Println(names) 106 | a := names[0:2] 107 | b := names[1:3] 108 | fmt.Println(a, b) 109 | 110 | b[0] = "XXX" // update copied slice will affect the original array 111 | fmt.Println(names) 112 | fmt.Println(a, b) 113 | 114 | // Output: 115 | // [John Paul George Ringo] 116 | // [John Paul] [Paul George] 117 | // [John XXX George Ringo] 118 | // [John XXX] [XXX George] 119 | } 120 | 121 | func ExampleSliceLiterals() { 122 | q := []int{2, 3, 5, 7, 11, 13} 123 | fmt.Println(q) 124 | 125 | r := []bool{true, false, true, true, false, true} 126 | fmt.Println(r) 127 | 128 | s := []struct { 129 | i int 130 | b bool 131 | }{ 132 | {2, true}, 133 | {3, false}, 134 | {5, true}, 135 | {7, true}, 136 | {11, false}, 137 | {13, true}, 138 | } 139 | fmt.Println(s) 140 | 141 | // Output: 142 | // [2 3 5 7 11 13] 143 | // [true false true true false true] 144 | // [{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}] 145 | } 146 | 147 | func ExampleSliceDefaults() { 148 | s := []int{2, 3, 5, 7, 11, 13} 149 | 150 | s = s[1:4] 151 | fmt.Println(s) 152 | 153 | s = s[:2] 154 | fmt.Println(s) 155 | 156 | s = s[1:] 157 | fmt.Println(s) 158 | 159 | // Output: 160 | // [3 5 7] 161 | // [3 5] 162 | // [5] 163 | } 164 | 165 | func printSlice(s []int) { 166 | fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) 167 | } 168 | 169 | func ExampleSliceLengthAndCapacity() { 170 | s := []int{2, 3, 5, 7, 11, 13} 171 | 172 | s = s[:0] 173 | printSlice(s) 174 | 175 | s = s[:4] // extends its length 176 | printSlice(s) 177 | 178 | s = s[2:] // drop its first two values 179 | printSlice(s) 180 | 181 | // Output: 182 | // len=0 cap=6 [] 183 | // len=4 cap=6 [2 3 5 7] 184 | // len=2 cap=4 [5 7] 185 | } 186 | 187 | func ExampleNilSlices() { 188 | var s []int // nil slice has a lanegth and capacity of 0 and has no underlying array 189 | fmt.Println(s, len(s), cap(s)) 190 | 191 | if s == nil { 192 | fmt.Println("nil!") 193 | } 194 | 195 | // Output: 196 | // [] 0 0 197 | // nil! 198 | } 199 | 200 | func printSliceWithString(s string, x []int) { 201 | fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) 202 | } 203 | 204 | func ExampleCreatingASliceWithMake() { 205 | a := make([]int, 5) 206 | printSliceWithString("a", a) 207 | 208 | b := make([]int, 0, 5) 209 | printSliceWithString("b", b) 210 | 211 | c := b[:2] /** with first two elements */ 212 | printSliceWithString("c", c) 213 | 214 | d := c[2:5] 215 | printSliceWithString("d", d) 216 | 217 | // Output: 218 | // a len=5 cap=5 [0 0 0 0 0] 219 | // b len=0 cap=5 [] 220 | // c len=2 cap=5 [0 0] 221 | // d len=3 cap=3 [0 0 0] 222 | } 223 | 224 | func ExampleSlicesOfSlices() { 225 | board := [][]string{ 226 | []string{"_", "_", "_"}, 227 | []string{"_", "_", "_"}, 228 | []string{"_", "_", "_"}, 229 | } 230 | 231 | board[0][0] = "X" 232 | board[2][2] = "O" 233 | board[1][2] = "X" 234 | board[1][0] = "O" 235 | board[0][2] = "X" 236 | 237 | for i := 0; i < len(board); i++ { 238 | fmt.Printf("%s\n", strings.Join(board[i], " ")) 239 | } 240 | 241 | // Output: 242 | // X _ X 243 | // O _ X 244 | // _ _ O 245 | } 246 | 247 | func ExampleAppendingToASlice() { 248 | var s []int 249 | printSlice(s) 250 | 251 | s = append(s, 0) 252 | printSlice(s) 253 | 254 | s = append(s, 1) 255 | printSlice(s) 256 | 257 | s = append(s, 2, 3, 4) 258 | printSlice(s) 259 | 260 | // Output: 261 | // len=0 cap=0 [] 262 | // len=1 cap=1 [0] 263 | // len=2 cap=2 [0 1] 264 | // len=5 cap=6 [0 1 2 3 4] 265 | } 266 | 267 | func ExampleRange() { 268 | var pow = []int{1, 2, 4, 8, 16} 269 | for i, v := range pow { 270 | fmt.Printf("2**%d = %d\n", i, v) 271 | } 272 | 273 | // Output: 274 | // 2**0 = 1 275 | // 2**1 = 2 276 | // 2**2 = 4 277 | // 2**3 = 8 278 | // 2**4 = 16 279 | } 280 | 281 | func ExampleRangeContinued() { 282 | var pow = []int{0, 0, 0, 0, 0} 283 | 284 | for i := range pow { 285 | pow[i] = 1 << uint(i) // == 2 ** i 286 | } 287 | 288 | for _, value := range pow { 289 | fmt.Printf("%d\n", value) 290 | } 291 | 292 | // Output: 293 | // 1 294 | // 2 295 | // 4 296 | // 8 297 | // 16 298 | } 299 | 300 | type Vertex2 struct { 301 | Lat, Long float64 302 | } 303 | 304 | func ExampleMaps() { 305 | var m map[string]Vertex2 306 | m = make(map[string]Vertex2) 307 | 308 | m["Bell Labs"] = Vertex2{ 309 | 40.68433, -74.39967, 310 | } 311 | 312 | fmt.Println(m["Bell Labs"]) 313 | 314 | // Output: {40.68433 -74.39967} 315 | } 316 | 317 | func ExampleMapLiterals() { 318 | m := map[string]Vertex2{ 319 | "Bell Labs": Vertex2{ 320 | 40.68433, -74.39967, 321 | }, 322 | "Google": { /** if the type is top-level you can ommit the type name */ 323 | 37.42202, -122.08408, 324 | }, 325 | } 326 | 327 | fmt.Println(m) 328 | 329 | // Output: 330 | // map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}] 331 | } 332 | 333 | func ExampleMutatingMaps() { 334 | m := make(map[string]int) 335 | 336 | m["Answer"] = 42 337 | fmt.Println(m["Answer"]) 338 | 339 | m["Answer"] = 48 340 | fmt.Println(m["Answer"]) 341 | 342 | delete(m, "Answer") 343 | fmt.Println(m["Answer"]) 344 | 345 | v, ok := m["Answer"] 346 | fmt.Println(v, ok) 347 | 348 | // Output: 349 | // 42 350 | // 48 351 | // 0 352 | // 0 false 353 | } 354 | 355 | func ExampleWordCount() { 356 | input := "hello world 1ambda world" 357 | words := strings.Fields(input) 358 | 359 | wordToCount := make(map[string]int) 360 | 361 | for i := range words { 362 | word := words[i] 363 | if _, ok := wordToCount[word]; !ok { 364 | wordToCount[word] = 0 365 | } 366 | 367 | wordToCount[word] += 1 368 | } 369 | 370 | fmt.Println(wordToCount["world"]) 371 | 372 | // Output: 2 373 | } 374 | 375 | func ExampleFunctionValues() { 376 | compute := func(fn func(float64, float64) float64) float64 { 377 | return fn(3, 4) 378 | } 379 | 380 | hypot := func(x, y float64) float64 { 381 | return math.Sqrt(x*x + y*y) 382 | } 383 | 384 | fmt.Println(hypot(3, 4)) 385 | fmt.Println(compute(hypot)) 386 | fmt.Println(compute(math.Pow)) 387 | 388 | // Output: 389 | // 5 390 | // 5 391 | // 81 392 | } 393 | 394 | func ExampleFunctionClosures() { 395 | adder := func() func(int) int { 396 | sum := 0 397 | 398 | return func(x int) int { 399 | sum += x 400 | return sum 401 | } 402 | } 403 | 404 | pos, neg := adder(), adder() 405 | 406 | for i := 0; i < 5; i++ { 407 | fmt.Println(pos(i), neg(-2*i)) 408 | } 409 | 410 | // Output: 411 | // 0 0 412 | // 1 -2 413 | // 3 -6 414 | // 6 -12 415 | // 10 -20 416 | } 417 | 418 | func ExampleFibonacci() { 419 | genFibo := func() func() int { 420 | a, b := 0, 1 421 | 422 | return func() int { 423 | temp := a 424 | 425 | a = b 426 | b = temp + a 427 | 428 | return temp 429 | } 430 | } 431 | 432 | fibo := genFibo() 433 | 434 | for i := 0; i < 6; i++ { 435 | fmt.Println(fibo()) 436 | } 437 | 438 | // Output: 439 | // 0 440 | // 1 441 | // 1 442 | // 2 443 | // 3 444 | // 5 445 | } 446 | -------------------------------------------------------------------------------- /channel/curious-channels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func WaitMany(a, b chan bool) { 9 | for a != nil || b!= nil { 10 | select { 11 | case <-a: 12 | a = nil 13 | case <-b: 14 | b = nil 15 | } 16 | } 17 | } 18 | 19 | func main() { 20 | a, b := make(chan bool), make (chan bool) 21 | t0 := time.Now() 22 | 23 | go func() { 24 | close(a) 25 | close(b) 26 | }() 27 | 28 | WaitMany(a, b) 29 | fmt.Printf("waited %v for WaitMany\n", time.Since(t0)) 30 | } 31 | -------------------------------------------------------------------------------- /concurrency-is-not-parallelism/load-balancer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/heap" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // ref - https://talks.golang.org/2012/waza.slide#46 10 | 11 | func main() { 12 | rand.Seed(time.Now().Unix()) 13 | 14 | } 15 | 16 | type Request struct { 17 | fn func() int // The operation to perform. 18 | c chan int 19 | } 20 | 21 | // Worker keps a channel of reqs, some load tracking data 22 | type Worker struct { 23 | requests chan Request // work to do (buffered channel) 24 | pending int // count of pending tasks 25 | index int // index in the heap 26 | } 27 | 28 | type Pool []*Worker 29 | 30 | type Balancer struct { 31 | pool Pool 32 | done chan *Worker 33 | } 34 | 35 | func (b *Balancer) balance(work chan Request) { 36 | for { 37 | select { 38 | case req := <-work: 39 | b.dispatch(req) 40 | case w := <-b.done: 41 | b.complete(w) 42 | } 43 | } 44 | } 45 | 46 | func (p Pool) Less(i, j int) bool { 47 | return p[i].pending < p[j].pending 48 | } 49 | 50 | func (b *Balancer) dispatch(req Request) { 51 | w := heap.Pop(&b.pool).(*Worker) 52 | w.requests <- req 53 | w.pending++ 54 | heap.Push(&b.pool, w) 55 | } 56 | 57 | func (b *Balancer) complete(w *Worker) { 58 | w.pending-- 59 | heap.Remove(&b.pool, w.index) 60 | heap.Push(&b.pool, w) // Put it into its place on the heap 61 | } 62 | 63 | func (w *Worker) work(done chan *Worker) { 64 | for { 65 | req := <-w.requests 66 | req.c <- req.fn() 67 | done <- w 68 | } 69 | } 70 | 71 | func Requester(work chan<- Request) { 72 | c := make(chan int) 73 | 74 | for { 75 | Sleep() 76 | work <- Request{workFn, c} 77 | result := <-c 78 | } 79 | } 80 | 81 | // Sleep kills some time (fake load) 82 | func Sleep() { 83 | nWorker := 10 84 | sec := rand.Int63n(int64(nWorker * 2)) 85 | time.Sleep(time.Duration(sec) * time.Second) 86 | } 87 | 88 | func workFn() int { 89 | millis := rand.Intn(1000) 90 | time.Sleep(time.Duration(millis) * time.Second) 91 | return 0 92 | } 93 | -------------------------------------------------------------------------------- /concurrency-patterns/00_basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | c := make(chan string) 11 | go boring("boring!", c) 12 | 13 | for i := 0; i < 5; i++ { 14 | fmt.Printf("You say: %q\n", <-c) 15 | } 16 | 17 | fmt.Println("You're boring; I'm leaving") 18 | 19 | } 20 | 21 | func boring(msg string, c chan string) { 22 | for i := 0; ; i++ { 23 | c <- fmt.Sprintf("%s %d", msg, i) 24 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /concurrency-patterns/01_generator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | joe := boring("Joe") 11 | ann := boring("Ann") 12 | 13 | for i := 0; i < 5; i++ { 14 | fmt.Printf("Joe say: %q\n", <-joe) 15 | fmt.Printf("Ann say: %q\n", <-ann) 16 | } 17 | 18 | fmt.Println("You're boring; I'm leaving") 19 | } 20 | 21 | func boring(msg string) chan string { 22 | c := make(chan string) 23 | 24 | go func() { 25 | for i := 0; ; i++ { 26 | c <- fmt.Sprintf("%s %d", msg, i) 27 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 28 | } 29 | }() 30 | 31 | return c 32 | } 33 | -------------------------------------------------------------------------------- /concurrency-patterns/02_fan_in.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rand.Seed(time.Now().UTC().UnixNano()) 11 | 12 | c := fanIn(boring("Joe"), boring("Ann")) 13 | 14 | for i := 0; i < 10; i++ { 15 | fmt.Println(<-c) 16 | } 17 | 18 | fmt.Println("You're boring; I'm leaving") 19 | } 20 | 21 | func fanIn(inputs ...<-chan string) <-chan string { 22 | c := make(chan string) 23 | 24 | for i := range inputs { 25 | input := inputs[i] 26 | 27 | go func() { 28 | for { 29 | c <- <-input 30 | } 31 | }() 32 | } 33 | 34 | return c 35 | } 36 | 37 | func boring(msg string) chan string { 38 | c := make(chan string) 39 | 40 | go func() { 41 | for i := 0; ; i++ { 42 | c <- fmt.Sprintf("%s %d", msg, i) 43 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 44 | } 45 | }() 46 | 47 | return c 48 | } 49 | -------------------------------------------------------------------------------- /concurrency-patterns/03_restoring_sequence.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type Message struct { 10 | msg string 11 | wait chan bool 12 | } 13 | 14 | func main() { 15 | rand.Seed(time.Now().UTC().UnixNano()) 16 | 17 | c := fanIn(boring("Joe"), boring("Ann")) 18 | 19 | for i := 0; i < 5; i++ { 20 | msg1 := <-c 21 | msg2 := <-c 22 | fmt.Println(msg1.msg) 23 | fmt.Println(msg2.msg) 24 | msg1.wait <- true 25 | msg2.wait <- true 26 | } 27 | 28 | fmt.Println("You're boring; I'm leaving") 29 | } 30 | 31 | func fanIn(inputs ...<-chan Message) <-chan Message { 32 | c := make(chan Message) 33 | 34 | for i := range inputs { 35 | input := inputs[i] 36 | go func() { 37 | for { 38 | c <- <-input 39 | } 40 | }() 41 | } 42 | 43 | return c 44 | } 45 | 46 | func boring(msg string) <-chan Message { 47 | c := make(chan Message) 48 | wait := make(chan bool) 49 | 50 | go func() { 51 | for i := 0; ; i++ { 52 | c <- Message{fmt.Sprintf("%s %d", msg, i), wait} 53 | time.Sleep(time.Duration(rand.Intn(2e3)) * time.Millisecond) 54 | <-wait 55 | } 56 | }() 57 | 58 | return c 59 | } 60 | -------------------------------------------------------------------------------- /concurrency-patterns/04_select_timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rand.Seed(time.Now().UTC().UnixNano()) 11 | 12 | c := boring("Joe") 13 | timeout := time.After(5 * time.Second) 14 | 15 | for { 16 | select { 17 | case s := <-c: 18 | fmt.Println(s) 19 | case <-timeout: 20 | fmt.Println("You're too slow") 21 | return 22 | } 23 | } 24 | } 25 | 26 | func boring(msg string) chan string { 27 | c := make(chan string) 28 | 29 | go func() { 30 | for i := 0; ; i++ { 31 | c <- fmt.Sprintf("%s %d", msg, i) 32 | time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) 33 | } 34 | }() 35 | 36 | return c 37 | } 38 | -------------------------------------------------------------------------------- /concurrency-patterns/05_select_all_timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rand.Seed(time.Now().UTC().UnixNano()) 11 | 12 | c := boring("Joe") 13 | 14 | for { 15 | select { 16 | case s := <-c: 17 | fmt.Println(s) 18 | case <-time.After(1 * time.Second): 19 | fmt.Println("You're too slow") 20 | return 21 | } 22 | } 23 | } 24 | 25 | func boring(msg string) chan string { 26 | c := make(chan string) 27 | 28 | go func() { 29 | for i := 0; ; i++ { 30 | c <- fmt.Sprintf("%s %d", msg, i) 31 | time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) 32 | } 33 | }() 34 | 35 | return c 36 | } 37 | -------------------------------------------------------------------------------- /concurrency-patterns/06_quit_channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rand.Seed(time.Now().UTC().UnixNano()) 11 | 12 | quit := make(chan bool) 13 | c := boring("Joe", quit) 14 | 15 | for i := rand.Intn(5); i >= 0; i-- { 16 | fmt.Println(<-c) 17 | } 18 | 19 | quit <- true 20 | } 21 | 22 | func boring(msg string, quit chan bool) chan string { 23 | c := make(chan string) 24 | 25 | go func() { 26 | for i := 0; ; i++ { 27 | 28 | select { 29 | case c <- fmt.Sprintf("%s %d", msg, i): 30 | // do nothing 31 | case <-quit: 32 | fmt.Println("I'm full") 33 | return 34 | } 35 | 36 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 37 | } 38 | }() 39 | 40 | return c 41 | } 42 | -------------------------------------------------------------------------------- /concurrency-patterns/07_quit_channel_cleanup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rand.Seed(time.Now().UTC().UnixNano()) 11 | 12 | quit := make(chan string) 13 | c := boring("Joe", quit) 14 | 15 | for i := rand.Intn(5); i >= 0; i-- { 16 | fmt.Println(<-c) 17 | } 18 | 19 | quit <- "Finish it!" 20 | // do some cleanup 21 | fmt.Printf("Joe says: %q\n", <-quit) 22 | } 23 | 24 | func boring(msg string, quit chan string) chan string { 25 | c := make(chan string) 26 | 27 | go func() { 28 | for i := 0; ; i++ { 29 | 30 | select { 31 | case c <- fmt.Sprintf("%s %d", msg, i): 32 | // do nothing 33 | case <-quit: 34 | // do some cleanup 35 | quit <- "Thanks!" 36 | return 37 | } 38 | 39 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 40 | } 41 | }() 42 | 43 | return c 44 | } 45 | -------------------------------------------------------------------------------- /concurrency-patterns/08_daisy_chain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func f(left, right chan int) { 8 | left <- 1 + <-right 9 | } 10 | 11 | func main() { 12 | 13 | const n = 100000 14 | leftmost := make(chan int) 15 | right := leftmost 16 | left := leftmost 17 | 18 | // build chain sequencially from leftmost to right 19 | for i := 0; i < n; i++ { 20 | right = make(chan int) 21 | go f(left, right) 22 | left = right 23 | } 24 | 25 | // trigger 26 | go func(c chan int) { c <- 1 }(right) 27 | 28 | // get result 29 | fmt.Println(<-leftmost) 30 | } 31 | -------------------------------------------------------------------------------- /concurrency-patterns/11_google_search_v1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type Result string 10 | type Search func(query string) Result 11 | 12 | func getSearchSource(kind string) Search { 13 | return func(query string) Result { 14 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 15 | return Result(fmt.Sprintf("%s result for %q\n", kind, query)) 16 | } 17 | } 18 | 19 | func Google(query string) (results []Result) { 20 | var ( 21 | Web = getSearchSource("web") 22 | Image = getSearchSource("image") 23 | Video = getSearchSource("video") 24 | ) 25 | 26 | results = append(results, Web(query)) 27 | results = append(results, Image(query)) 28 | results = append(results, Video(query)) 29 | 30 | return 31 | } 32 | 33 | func main() { 34 | rand.Seed(time.Now().UnixNano()) 35 | 36 | // do search 37 | start := time.Now() 38 | results := Google("golang") 39 | elasped := time.Since(start) 40 | 41 | fmt.Println(results) 42 | fmt.Println(elasped) 43 | } 44 | -------------------------------------------------------------------------------- /concurrency-patterns/12_google_search_v2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type Result string 10 | type Search func(query string) Result 11 | 12 | func getSearchSource(kind string) Search { 13 | return func(query string) Result { 14 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 15 | return Result(fmt.Sprintf("%s result for %q\n", kind, query)) 16 | } 17 | } 18 | 19 | func Google(query string) (results []Result) { 20 | var ( 21 | Web = getSearchSource("web") 22 | Image = getSearchSource("image") 23 | Video = getSearchSource("video") 24 | ) 25 | 26 | c := make(chan Result) 27 | 28 | go func() { c <- Web(query) }() 29 | go func() { c <- Video(query) }() 30 | go func() { c <- Image(query) }() 31 | 32 | for i := 0; i < 3; i++ { 33 | result := <-c 34 | results = append(results, result) 35 | } 36 | 37 | return 38 | } 39 | 40 | func main() { 41 | rand.Seed(time.Now().UnixNano()) 42 | 43 | // do search 44 | start := time.Now() 45 | results := Google("golang") 46 | elasped := time.Since(start) 47 | 48 | fmt.Println(results) 49 | fmt.Println(elasped) 50 | } 51 | -------------------------------------------------------------------------------- /concurrency-patterns/13_google_search_v2.1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type Result string 10 | type Search func(query string) Result 11 | 12 | func getSearchSource(kind string) Search { 13 | return func(query string) Result { 14 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 15 | return Result(fmt.Sprintf("%s result for %q\n", kind, query)) 16 | } 17 | } 18 | 19 | func Google(query string) (results []Result) { 20 | var ( 21 | Web = getSearchSource("web") 22 | Image = getSearchSource("image") 23 | Video = getSearchSource("video") 24 | ) 25 | 26 | c := make(chan Result) 27 | 28 | go func() { c <- Web(query) }() 29 | go func() { c <- Video(query) }() 30 | go func() { c <- Image(query) }() 31 | 32 | timeout := time.After(80 * time.Millisecond) 33 | 34 | for i := 0; i < 3; i++ { 35 | select { 36 | case result := <-c: 37 | results = append(results, result) 38 | case <-timeout: 39 | fmt.Println("search timed out") 40 | return 41 | } 42 | } 43 | 44 | return 45 | } 46 | 47 | func main() { 48 | rand.Seed(time.Now().UnixNano()) 49 | 50 | // do search 51 | start := time.Now() 52 | results := Google("golang") 53 | elasped := time.Since(start) 54 | 55 | fmt.Println(results) 56 | fmt.Println(elasped) 57 | } 58 | -------------------------------------------------------------------------------- /concurrency-patterns/14_google_search_v3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type Result string 10 | type Search func(query string) Result 11 | type SearchGen func(kind string) Search 12 | 13 | func GetSearchSource(kind string) Search { 14 | return func(query string) Result { 15 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 16 | return Result(fmt.Sprintf("%s result for %q\n", kind, query)) 17 | } 18 | } 19 | 20 | func GetMultipleSearchSources(kind string, replica int) (replicas []Search) { 21 | for i := 0; i < replica; i++ { 22 | replicas = append(replicas, GetSearchSource(kind)) 23 | } 24 | 25 | return 26 | } 27 | 28 | func First(query string, searchs []Search) Result { 29 | c := make(chan Result) 30 | 31 | for i := range searchs { 32 | go func(i int) { c <- searchs[i](query) }(i) 33 | } 34 | 35 | return <-c 36 | } 37 | 38 | func Google(query string) (results []Result) { 39 | var ( 40 | webSources = GetMultipleSearchSources("web", 3) 41 | imageSources = GetMultipleSearchSources("image", 3) 42 | videoSources = GetMultipleSearchSources("video", 3) 43 | ) 44 | 45 | c := make(chan Result) 46 | 47 | go func() { c <- First(query, webSources) }() 48 | go func() { c <- First(query, imageSources) }() 49 | go func() { c <- First(query, videoSources) }() 50 | 51 | timeout := time.After(80 * time.Millisecond) 52 | 53 | for i := 0; i < 3; i++ { 54 | select { 55 | case result := <-c: 56 | results = append(results, result) 57 | case <-timeout: 58 | fmt.Println("search timed out") 59 | return 60 | } 61 | } 62 | 63 | return 64 | } 65 | 66 | func main() { 67 | rand.Seed(time.Now().UnixNano()) 68 | 69 | // do search 70 | start := time.Now() 71 | results := Google("golang") 72 | elasped := time.Since(start) 73 | 74 | fmt.Println(results) 75 | fmt.Println(elasped) 76 | } 77 | -------------------------------------------------------------------------------- /concurrency-patterns/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/1ambda/golang/concurrency-patterns 2 | import: [] 3 | -------------------------------------------------------------------------------- /defer-panic-recover/defer-panic-recover.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func buggyCopyFile(srcName, dstName string) (written int64, err error) { 10 | src, err := os.Open(srcName) 11 | if err != nil { 12 | return 13 | } 14 | 15 | // If os.create fails, `src` will not be closed 16 | dst, err := os.Create(dstName) 17 | if err != nil { 18 | return 19 | } 20 | dst.Close() 21 | src.Close() 22 | 23 | return io.Copy(dst, src) 24 | } 25 | 26 | func copyFile(srcName, dstName string) (written int64, err error) { 27 | src, err := os.Open(srcName) 28 | if err != nil { 29 | return 30 | } 31 | defer src.Close() 32 | 33 | dst, err := os.Create(dstName) 34 | if err != nil { 35 | return 36 | } 37 | defer dst.Close() 38 | 39 | return io.Copy(dst, src) 40 | } 41 | 42 | func main() { 43 | 44 | /** 45 | * A `defer` statement pushes a function call onto a list. 46 | * The list of saved calls is executed after the surrounding function returns. 47 | * 48 | * The behavior of defer statements is straightforward and predictable. 49 | * There are 3 simple rules. 50 | */ 51 | 52 | // 1. A deffered function's arguments are evaluated when the defer stmt is evaluated 53 | a := func() { 54 | i := 0 55 | defer fmt.Println(i) // i == 0 56 | i++ 57 | return 58 | } 59 | _ = a 60 | 61 | // 2. Deferred function calls are executed 62 | // in LIFO order after the surrounding function returns 63 | 64 | b := func() { 65 | for i := 0; i < 4; i++ { 66 | defer fmt.Print(i) // 3210 67 | } 68 | } 69 | _ = b 70 | 71 | // 3. Deferred functinos may read and assign to 72 | // the returning function's named return values 73 | 74 | c := func(i int) int { 75 | defer func() { i++ }() 76 | return i 77 | }(1) 78 | _ = c 79 | 80 | /** 81 | * `Panic` is a built in function that stops the ordinary flow of control and 82 | * begins panicking. When the function `F` calls panic, executino of F stops, 83 | * any deffered funcs in F are executed normally, and then F returns to its caller. 84 | * 85 | * To the caller, F then behaves like a call to panic. 86 | * The process continues up the stack until all functions in 87 | * the current goroutine have returned, at which point the program crashes. 88 | * 89 | * Panics can be initiated by invoking panic directly. 90 | * The can also be caused by runtime errors, such as out-of-bouns array accesses. 91 | */ 92 | 93 | /** 94 | * `Recover` is a built-in function tat regains control of a panicking goroutines. 95 | * Recover is only useful inside deferred functions. During normal execution, 96 | * a call to recover will return nil and have no other effect. 97 | * 98 | * If the current gorutine is panicking, a call to recover will capture the value given 99 | * to panic and resume normal execution. 100 | */ 101 | 102 | f() 103 | fmt.Println("Returned normally from f.") 104 | 105 | // Output: 106 | // (f) Caliing g. 107 | // (g) P 0 108 | // (g) P 1 109 | // (g) P 2 110 | // (g) P 3 111 | // (g) Panicking! 112 | // (g) Defer in g 3 113 | // (g) Defer in g 2 114 | // (g) Defer in g 1 115 | // (g) Defer in g 0 116 | // (f) Recovered in f 4 117 | // (main) returned normally from f 118 | } 119 | 120 | func f() { 121 | // If we remove the deferred function here, 122 | // `f` will be not recovered and reaches the top of the goroutine's stack, 123 | // terminating the program. 124 | defer func() { 125 | if r := recover(); r != nil { 126 | fmt.Println("Recovered in f", r) 127 | } 128 | }() 129 | 130 | fmt.Println("Calling g.") 131 | g(0) 132 | fmt.Println("Returned normally from g.") 133 | } 134 | 135 | func g(i int) { 136 | if i > 3 { 137 | fmt.Println("Panicking!") 138 | panic(fmt.Sprintf("%v", i)) 139 | } 140 | 141 | defer fmt.Println("Defer in g", i) 142 | fmt.Println("Printing in g", i) 143 | g(i + 1) 144 | } 145 | -------------------------------------------------------------------------------- /errors/error-handling-and-go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // `error` type is an interface 8 | // 9 | // type error interface { 10 | // Error() string 11 | // } 12 | 13 | // We can implement our own error type 14 | type myErrorString struct { 15 | s string 16 | } 17 | 18 | func (e *myErrorString) Error() string { 19 | return e.s 20 | } 21 | 22 | func new(text string) error { 23 | return &myErrorString{text} 24 | } 25 | 26 | // Since `error` is an interface, we can use arbitrary data structures 27 | type NegativeSqrtError float64 28 | 29 | func (f NegativeSqrtError) Error() string { 30 | return fmt.Sprintf("math: square root of negative number %g", float64(f)) 31 | } 32 | 33 | /** 34 | * We can use type assertion like 35 | * 36 | * if nerr, ok := err.(NetworkError); ok && nerr.Temporate() { ... } 37 | */ 38 | type NetworkError interface { 39 | error 40 | Timeout() bool 41 | Temporary() bool 42 | } 43 | 44 | // See more: https://golang.org/doc/faq#nil_error 45 | 46 | func main() { 47 | 48 | } 49 | -------------------------------------------------------------------------------- /go-for-java-programmers/google-search-frontend.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "html/template" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | http.HandleFunc("/search", handleSearch) 15 | fmt.Println("serving on http://localhost:7777/search") 16 | log.Fatal(http.ListenAndServe("localhost:7777", nil)) 17 | } 18 | 19 | type Result struct { 20 | Title, URL string 21 | } 22 | 23 | type templateData struct { 24 | Results []Result 25 | Elasped time.Duration 26 | } 27 | 28 | // handleSearch handles URLs like "/search?q=golang" by running a 29 | // Google search for "golang" and writing the results as HTML to w. 30 | func handleSearch(w http.ResponseWriter, req *http.Request) { 31 | log.Println("serving", req.URL) 32 | 33 | query := req.FormValue("q") 34 | 35 | if query == "" { 36 | http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest) 37 | return 38 | } 39 | 40 | start := time.Now() 41 | results, err := Search(query) 42 | elasped := time.Since(start) 43 | 44 | if err != nil { 45 | http.Error(w, err.Error(), http.StatusInternalServerError) 46 | return 47 | } 48 | 49 | var resultsTemplate = template.Must(template.New("results").Parse(` 50 | 51 | 52 | 53 |
    54 | {{range .Results}} 55 |
  1. {{.Title}} - {{.URL}}
  2. 56 | {{end}} 57 |
58 |

{{len .Results}} results

59 | 60 | 61 | `)) 62 | 63 | if err := resultsTemplate.Execute(w, templateData{ 64 | Results: results, 65 | Elasped: elasped, 66 | }); err != nil { 67 | log.Print(err) 68 | return 69 | } 70 | } 71 | 72 | func Search(query string) ([]Result, error) { 73 | u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0") 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | q := u.Query() 79 | q.Set("q", query) 80 | u.RawQuery = q.Encode() 81 | 82 | resp, err := http.Get(u.String()) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | defer resp.Body.Close() 88 | 89 | var jsonResponse struct { 90 | ResponseData struct { 91 | Results []struct { 92 | TitleNoFormatting, URL string 93 | } 94 | } 95 | } 96 | if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil { 97 | return nil, err 98 | } 99 | 100 | // Extract the Results from jsonResponse and return them. 101 | var results []Result 102 | for _, r := range jsonResponse.ResponseData.Results { 103 | results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL}) 104 | } 105 | return results, nil 106 | } 107 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | *.iml 4 | 5 | vendor/ 6 | gin-bin 7 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | httptransport "github.com/go-kit/kit/transport/http" 9 | 10 | "context" 11 | "github.com/1ambda/golang/go-kit-tutorial/stringsvc/endpoint" 12 | "github.com/1ambda/golang/go-kit-tutorial/stringsvc/service" 13 | "github.com/1ambda/golang/go-kit-tutorial/stringsvc/transport" 14 | "net/http" 15 | ) 16 | 17 | func main() { 18 | port := os.Getenv("PORT") 19 | if port == "" { 20 | port = "9090" 21 | } 22 | addr := fmt.Sprintf(":%s", port) 23 | 24 | ctx := context.Background() 25 | svc := service.StringServiceImpl{} 26 | 27 | uppercaseHandler := httptransport.NewServer( 28 | ctx, 29 | endpoint.MakeUppercaseEndpoint(svc), 30 | transport.DecodeUppercaseRequest, 31 | transport.EncodeResponse, 32 | ) 33 | 34 | countHandler := httptransport.NewServer( 35 | ctx, 36 | endpoint.MakeCountEndpoint(svc), 37 | transport.DecodeCountRequest, 38 | transport.EncodeResponse, 39 | ) 40 | 41 | http.Handle("/uppercase", uppercaseHandler) 42 | http.Handle("/count", countHandler) 43 | log.Printf("Starting :%s\n", port) 44 | log.Fatal(http.ListenAndServe(addr, nil)) 45 | } 46 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/endpoint/StringServiceEndpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | . "github.com/go-kit/kit/endpoint" 5 | . "golang.org/x/net/context" 6 | 7 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc/service" 8 | ) 9 | 10 | func MakeUppercaseEndpoint(svc StringService) Endpoint { 11 | return func(ctx Context, request interface{}) (interface{}, error) { 12 | req := request.(UppercaseRequest) 13 | v, err := svc.Uppercase(req.S) 14 | if err != nil { 15 | return UppercaseResponse{v, err.Error()}, nil 16 | } 17 | 18 | return UppercaseResponse{v, ""}, nil 19 | } 20 | } 21 | 22 | func MakeCountEndpoint(svc StringService) Endpoint { 23 | return func(ctx Context, request interface{}) (interface{}, error) { 24 | req := request.(CountRequest) 25 | v := svc.Count(req.S) 26 | return CountResponse{v}, nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/glide.lock: -------------------------------------------------------------------------------- 1 | hash: 79b48642b47e9a97743c082576de69d91a256a8c8badd8c7f173ce5c44f70efa 2 | updated: 2016-11-20T23:23:24.039059269+09:00 3 | imports: 4 | - name: github.com/codegangsta/gin 5 | version: 200f0a2e339b4cb9e44d37f678b45f2e983fec0d 6 | - name: github.com/go-kit/kit 7 | version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41 8 | subpackages: 9 | - endpoint 10 | - log 11 | - transport/http 12 | - name: github.com/go-logfmt/logfmt 13 | version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 14 | - name: github.com/go-stack/stack 15 | version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 16 | - name: github.com/kr/logfmt 17 | version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 18 | - name: github.com/pkg/errors 19 | version: 645ef00459ed84a119197bfb8d8205042c6df63d 20 | - name: golang.org/x/net 21 | version: 4971afdc2f162e82d185353533d3cf16188a9f4e 22 | subpackages: 23 | - context 24 | - context/ctxhttp 25 | testImports: [] 26 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/1ambda/golang/go-kit-tutorial/stringsvc 2 | import: 3 | - package: github.com/go-kit/kit 4 | version: v0.3.0 5 | - package: github.com/pkg/errors 6 | version: v0.8.0 7 | - package: golang.org/x/net 8 | subpackages: 9 | - context 10 | - package: github.com/go-logfmt/logfmt 11 | version: v0.3.0 12 | - package: github.com/codegangsta/gin 13 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/service/StringService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | type StringService interface { 10 | Uppercase(string) (string, error) 11 | Count(string) int 12 | } 13 | 14 | type StringServiceImpl struct{} 15 | 16 | func (StringServiceImpl) Uppercase(s string) (string, error) { 17 | if s == "" { 18 | return "", errors.New("Empty string") 19 | } 20 | 21 | return strings.ToUpper(s), nil 22 | } 23 | 24 | func (StringServiceImpl) Count(s string) int { 25 | return len(s) 26 | } 27 | 28 | type UppercaseRequest struct { 29 | S string `json:"s"` 30 | } 31 | 32 | type UppercaseResponse struct { 33 | V string `json:"v"` 34 | Err string `json:"err,omitempty"` 35 | } 36 | 37 | type CountRequest struct { 38 | S string `json:"s"` 39 | } 40 | 41 | type CountResponse struct { 42 | V int `json:"v"` 43 | } 44 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc/transport/StringServiceTransport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | . "golang.org/x/net/context" 5 | 6 | "encoding/json" 7 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc/service" 8 | "github.com/pkg/errors" 9 | "net/http" 10 | ) 11 | 12 | func DecodeUppercaseRequest(_ Context, r *http.Request) (interface{}, error) { 13 | var request UppercaseRequest 14 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 15 | return nil, errors.Wrap(err, "Failed to decode UppercaseRequest") 16 | } 17 | return request, nil 18 | } 19 | 20 | func DecodeCountRequest(_ Context, r *http.Request) (interface{}, error) { 21 | var request CountRequest 22 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 23 | return nil, errors.Wrap(err, "Failed to decode CountRequest") 24 | } 25 | return request, nil 26 | } 27 | 28 | func EncodeResponse(_ Context, w http.ResponseWriter, response interface{}) error { 29 | return json.NewEncoder(w).Encode(response) 30 | } 31 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | *.iml 4 | 5 | vendor/ 6 | gin-bin 7 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/Server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | glog "log" 6 | "os" 7 | 8 | httptransport "github.com/go-kit/kit/transport/http" 9 | 10 | "context" 11 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc2/endpoint" 12 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc2/service" 13 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc2/transport" 14 | "github.com/go-kit/kit/log" 15 | "net/http" 16 | 17 | kitprometheus "github.com/go-kit/kit/metrics/prometheus" 18 | stdprometheus "github.com/prometheus/client_golang/prometheus" 19 | ) 20 | 21 | func main() { 22 | port := os.Getenv("PORT") 23 | if port == "" { 24 | port = "9090" 25 | } 26 | addr := fmt.Sprintf(":%s", port) 27 | metricGroup := "my_group" 28 | metricSystem := "string_servie" 29 | 30 | logger := log.NewLogfmtLogger(os.Stderr) 31 | fieldKeys := []string{"method", "error"} 32 | requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ 33 | Namespace: metricGroup, 34 | Subsystem: metricSystem, 35 | Name: "request_count", 36 | Help: "Number of requests received", 37 | }, fieldKeys) 38 | requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 39 | Namespace: metricGroup, 40 | Subsystem: metricSystem, 41 | Name: "request_latency_microseconds", 42 | Help: "Total duration of requests in microseconds.", 43 | }, fieldKeys) 44 | countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 45 | Namespace: metricGroup, 46 | Subsystem: metricSystem, 47 | Name: "count_result", 48 | Help: "The result of each count method.", 49 | }, []string{}) // no fields here 50 | 51 | ctx := context.Background() 52 | var svc StringService 53 | svc = StringServiceImpl{} 54 | svc = LoggingMiddleware{logger, svc} 55 | svc = InstrumentMiddleware{requestCount, requestLatency, countResult, svc} 56 | 57 | uppercaseHandler := httptransport.NewServer( 58 | ctx, 59 | MakeUppercaseEndpoint(svc), 60 | DecodeUppercaseRequest, 61 | EncodeResponse, 62 | ) 63 | 64 | countHandler := httptransport.NewServer( 65 | ctx, 66 | MakeCountEndpoint(svc), 67 | DecodeCountRequest, 68 | EncodeResponse, 69 | ) 70 | 71 | http.Handle("/uppercase", uppercaseHandler) 72 | http.Handle("/count", countHandler) 73 | http.Handle("/metrics", stdprometheus.Handler()) 74 | glog.Printf("Starting :%s\n", port) 75 | glog.Fatal(http.ListenAndServe(addr, nil)) 76 | } 77 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/endpoint/StringServiceEndpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | . "github.com/go-kit/kit/endpoint" 5 | . "golang.org/x/net/context" 6 | 7 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc2/service" 8 | ) 9 | 10 | func MakeUppercaseEndpoint(svc StringService) Endpoint { 11 | return func(ctx Context, request interface{}) (interface{}, error) { 12 | req := request.(UppercaseRequest) 13 | v, err := svc.Uppercase(req.S) 14 | if err != nil { 15 | return UppercaseResponse{v, err.Error()}, nil 16 | } 17 | 18 | return UppercaseResponse{v, ""}, nil 19 | } 20 | } 21 | 22 | func MakeCountEndpoint(svc StringService) Endpoint { 23 | return func(ctx Context, request interface{}) (interface{}, error) { 24 | req := request.(CountRequest) 25 | v := svc.Count(req.S) 26 | return CountResponse{v}, nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/glide.lock: -------------------------------------------------------------------------------- 1 | hash: 79b48642b47e9a97743c082576de69d91a256a8c8badd8c7f173ce5c44f70efa 2 | updated: 2016-11-20T23:23:24.039059269+09:00 3 | imports: 4 | - name: github.com/codegangsta/gin 5 | version: 200f0a2e339b4cb9e44d37f678b45f2e983fec0d 6 | - name: github.com/go-kit/kit 7 | version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41 8 | subpackages: 9 | - endpoint 10 | - log 11 | - transport/http 12 | - name: github.com/go-logfmt/logfmt 13 | version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 14 | - name: github.com/go-stack/stack 15 | version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 16 | - name: github.com/kr/logfmt 17 | version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 18 | - name: github.com/pkg/errors 19 | version: 645ef00459ed84a119197bfb8d8205042c6df63d 20 | - name: golang.org/x/net 21 | version: 4971afdc2f162e82d185353533d3cf16188a9f4e 22 | subpackages: 23 | - context 24 | - context/ctxhttp 25 | testImports: [] 26 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/1ambda/golang/go-kit-tutorial/stringsvc2 2 | import: 3 | - package: github.com/go-kit/kit 4 | version: v0.3.0 5 | - package: github.com/pkg/errors 6 | version: v0.8.0 7 | - package: golang.org/x/net 8 | subpackages: 9 | - context 10 | - package: github.com/go-logfmt/logfmt 11 | version: v0.3.0 12 | - package: github.com/codegangsta/gin 13 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/service/InstrumentMiddleware.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-kit/kit/metrics" 6 | "time" 7 | ) 8 | 9 | type InstrumentMiddleware struct { 10 | RequestCount metrics.Counter 11 | RequestLatency metrics.Histogram 12 | CountResult metrics.Histogram 13 | Next StringService 14 | } 15 | 16 | func (mw InstrumentMiddleware) Uppercase(s string) (output string, err error) { 17 | defer func(begin time.Time) { 18 | lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)} 19 | mw.RequestCount.With(lvs...).Add(1) 20 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 21 | }(time.Now()) 22 | 23 | output, err = mw.Next.Uppercase(s) 24 | return 25 | } 26 | 27 | func (mw InstrumentMiddleware) Count(s string) (n int) { 28 | defer func(begin time.Time) { 29 | lvs := []string{"method", "count", "error", "false"} 30 | mw.RequestCount.With(lvs...).Add(1) 31 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 32 | mw.CountResult.Observe(float64(n)) 33 | }(time.Now()) 34 | 35 | n = mw.Next.Count(s) 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/service/Logging.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Logger log.Logger 10 | Next StringService 11 | } 12 | 13 | func (mw LoggingMiddleware) Uppercase(s string) (output string, err error) { 14 | defer func(begin time.Time) { 15 | mw.Logger.Log( 16 | "method", "uppercase", 17 | "input", s, 18 | "output", output, 19 | "err", err, 20 | "took", time.Since(begin), 21 | ) 22 | }(time.Now()) 23 | 24 | output, err = mw.Next.Uppercase(s) 25 | return 26 | } 27 | 28 | func (mw LoggingMiddleware) Count(s string) (n int) { 29 | defer func(begin time.Time) { 30 | mw.Logger.Log( 31 | "method", "count", 32 | "input", s, 33 | "n", n, 34 | "took", time.Since(begin), 35 | ) 36 | }(time.Now()) 37 | 38 | return mw.Next.Count(s) 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/service/StringService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | type StringService interface { 10 | Uppercase(string) (string, error) 11 | Count(string) int 12 | } 13 | 14 | type StringServiceImpl struct{} 15 | 16 | func (StringServiceImpl) Uppercase(s string) (string, error) { 17 | if s == "" { 18 | return "", errors.New("Empty string") 19 | } 20 | 21 | return strings.ToUpper(s), nil 22 | } 23 | 24 | func (StringServiceImpl) Count(s string) int { 25 | return len(s) 26 | } 27 | 28 | type UppercaseRequest struct { 29 | S string `json:"s"` 30 | } 31 | 32 | type UppercaseResponse struct { 33 | V string `json:"v"` 34 | Err string `json:"err,omitempty"` 35 | } 36 | 37 | type CountRequest struct { 38 | S string `json:"s"` 39 | } 40 | 41 | type CountResponse struct { 42 | V int `json:"v"` 43 | } 44 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc2/transport/StringServiceTransport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | . "golang.org/x/net/context" 5 | 6 | "encoding/json" 7 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc2/service" 8 | "github.com/pkg/errors" 9 | "net/http" 10 | ) 11 | 12 | func DecodeUppercaseRequest(_ Context, r *http.Request) (interface{}, error) { 13 | var request UppercaseRequest 14 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 15 | return nil, errors.Wrap(err, "Failed to decode UppercaseRequest") 16 | } 17 | return request, nil 18 | } 19 | 20 | func DecodeCountRequest(_ Context, r *http.Request) (interface{}, error) { 21 | var request CountRequest 22 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 23 | return nil, errors.Wrap(err, "Failed to decode CountRequest") 24 | } 25 | return request, nil 26 | } 27 | 28 | func EncodeResponse(_ Context, w http.ResponseWriter, response interface{}) error { 29 | return json.NewEncoder(w).Encode(response) 30 | } 31 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | *.iml 4 | 5 | vendor/ 6 | gin-bin 7 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/Server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/go-kit/kit/log" 9 | stdprometheus "github.com/prometheus/client_golang/prometheus" 10 | "golang.org/x/net/context" 11 | 12 | . "github.com/1ambda/golang/go-kit-tutorial/stringsvc3/service" 13 | ) 14 | 15 | func main() { 16 | 17 | var ( 18 | listen = flag.String("listen", ":8080", "HTTP listen address") 19 | proxy = flag.String("proxy", "", "Optional comma-separated list of URLS to proxy requests") 20 | ) 21 | flag.Parse() 22 | 23 | var logger log.Logger 24 | logger = log.NewLogfmtLogger(os.Stderr) 25 | logger = log.NewContext(logger).With("listen", *listen).With("caller", log.DefaultCaller) 26 | ctx := context.Background() 27 | 28 | var svc StringService 29 | svc = StringServiceImpl{} 30 | svc = CreateUppercaseProxyMiddleware(*proxy, ctx, logger)(svc) 31 | svc = CreateLoggingMiddleware(logger)(svc) 32 | svc = CreatePrebuiltInstrumentMiddleware()(svc) 33 | 34 | uppercaseHandler := CreateUppercaseHandler(ctx, svc) 35 | countHandler := CreateCountHandler(ctx, svc) 36 | 37 | http.Handle("/uppercase", uppercaseHandler) 38 | http.Handle("/count", countHandler) 39 | http.Handle("/metrics", stdprometheus.Handler()) 40 | logger.Log("msg", "HTTP", "addr", *listen) 41 | logger.Log("err", http.ListenAndServe(*listen, nil)) 42 | } 43 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/glide.lock: -------------------------------------------------------------------------------- 1 | hash: 10ce2dc7162dd421cea5eb850c2aee208e0e2ab925f4301e0813c77baa259ddc 2 | updated: 2016-11-26T00:22:36.800677673+09:00 3 | imports: 4 | - name: github.com/1ambda/golang 5 | version: 6871b0d04be1bf2f294c406a85885bcb798025ac 6 | subpackages: 7 | - go-kit-tutorial/stringsvc2/endpoint 8 | - go-kit-tutorial/stringsvc2/service 9 | - go-kit-tutorial/stringsvc2/transport 10 | - name: github.com/afex/hystrix-go 11 | version: 39520ddd07a9d9a071d615f7476798659f5a3b89 12 | subpackages: 13 | - hystrix 14 | - hystrix/metric_collector 15 | - hystrix/rolling 16 | - name: github.com/beorn7/perks 17 | version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 18 | subpackages: 19 | - quantile 20 | - name: github.com/codegangsta/gin 21 | version: 200f0a2e339b4cb9e44d37f678b45f2e983fec0d 22 | - name: github.com/go-kit/kit 23 | version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41 24 | subpackages: 25 | - circuitbreaker 26 | - endpoint 27 | - log 28 | - metrics 29 | - metrics/internal/lv 30 | - metrics/prometheus 31 | - sd 32 | - transport/http 33 | - name: github.com/go-logfmt/logfmt 34 | version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 35 | - name: github.com/go-stack/stack 36 | version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 37 | - name: github.com/golang/protobuf 38 | version: 8ee79997227bf9b34611aee7946ae64735e6fd93 39 | subpackages: 40 | - proto 41 | - name: github.com/juju/ratelimit 42 | version: 77ed1c8a01217656d2080ad51981f6e99adaa177 43 | - name: github.com/kr/logfmt 44 | version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 45 | - name: github.com/matttproud/golang_protobuf_extensions 46 | version: c12348ce28de40eed0136aa2b644d0ee0650e56c 47 | subpackages: 48 | - pbutil 49 | - name: github.com/pkg/errors 50 | version: 645ef00459ed84a119197bfb8d8205042c6df63d 51 | - name: github.com/prometheus/client_golang 52 | version: c5b7fccd204277076155f10851dad72b76a49317 53 | subpackages: 54 | - prometheus 55 | - name: github.com/prometheus/client_model 56 | version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6 57 | subpackages: 58 | - go 59 | - name: github.com/prometheus/common 60 | version: 0d5de9d6d8629cb8bee6d4674da4127cd8b615a3 61 | subpackages: 62 | - expfmt 63 | - internal/bitbucket.org/ww/goautoneg 64 | - model 65 | - name: github.com/prometheus/procfs 66 | version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 67 | - name: github.com/sony/gobreaker 68 | version: 809bcd5a528f344e95c2368796c9225d128f9b3e 69 | - name: github.com/streadway/handy 70 | version: f450267a206e480d863d2a92846a40f6e2896b2a 71 | subpackages: 72 | - breaker 73 | - name: golang.org/x/net 74 | version: 4971afdc2f162e82d185353533d3cf16188a9f4e 75 | subpackages: 76 | - context 77 | - context/ctxhttp 78 | testImports: [] 79 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/1ambda/golang/go-kit-tutorial/stringsvc3 2 | import: 3 | - package: github.com/go-kit/kit 4 | version: v0.3.0 5 | - package: github.com/pkg/errors 6 | version: v0.8.0 7 | - package: golang.org/x/net 8 | subpackages: 9 | - context 10 | - package: github.com/go-logfmt/logfmt 11 | version: v0.3.0 12 | - package: github.com/codegangsta/gin 13 | - package: github.com/juju/ratelimit 14 | - package: github.com/sony/gobreaker 15 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Endpoint.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/go-kit/kit/endpoint" 5 | "github.com/go-kit/kit/transport/http" 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | func createUppercaseEndpoint(svc StringService) endpoint.Endpoint { 10 | return func(ctx context.Context, request interface{}) (interface{}, error) { 11 | req := request.(UppercaseRequest) 12 | v, err := svc.Uppercase(req.S) 13 | if err != nil { 14 | return UppercaseResponse{v, err.Error()}, nil 15 | } 16 | 17 | return UppercaseResponse{v, ""}, nil 18 | } 19 | } 20 | 21 | func CreateUppercaseHandler(ctx context.Context, svc StringService) *http.Server { 22 | return http.NewServer( 23 | ctx, 24 | createUppercaseEndpoint(svc), 25 | DecodeUppercaseRequest, 26 | EncodeResponse, 27 | ) 28 | } 29 | 30 | func createCountEndpoint(svc StringService) endpoint.Endpoint { 31 | return func(ctx context.Context, request interface{}) (interface{}, error) { 32 | req := request.(CountRequest) 33 | v := svc.Count(req.S) 34 | return CountResponse{v}, nil 35 | } 36 | } 37 | 38 | func CreateCountHandler(ctx context.Context, svc StringService) *http.Server { 39 | return http.NewServer( 40 | ctx, 41 | createCountEndpoint(svc), 42 | DecodeCountRequest, 43 | EncodeResponse, 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Instrument.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/go-kit/kit/metrics" 8 | kitprometheus "github.com/go-kit/kit/metrics/prometheus" 9 | stdprometheus "github.com/prometheus/client_golang/prometheus" 10 | ) 11 | 12 | type instrumentMiddleware struct { 13 | requestCount metrics.Counter 14 | requestLatency metrics.Histogram 15 | countResult metrics.Histogram 16 | StringService 17 | } 18 | 19 | func CreatePrebuiltInstrumentMiddleware() StringMiddleware { 20 | metricGroup := "my_group" 21 | metricSystem := "string_servie" 22 | 23 | fieldKeys := []string{"method", "error"} 24 | requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ 25 | Namespace: metricGroup, 26 | Subsystem: metricSystem, 27 | Name: "request_count", 28 | Help: "Number of requests received", 29 | }, fieldKeys) 30 | requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 31 | Namespace: metricGroup, 32 | Subsystem: metricSystem, 33 | Name: "request_latency_microseconds", 34 | Help: "Total duration of requests in microseconds.", 35 | }, fieldKeys) 36 | countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 37 | Namespace: metricGroup, 38 | Subsystem: metricSystem, 39 | Name: "count_result", 40 | Help: "The result of each count method.", 41 | }, []string{}) // no fields here 42 | 43 | return CreateInstrumentMiddleware( 44 | requestCount, 45 | requestLatency, 46 | countResult) 47 | } 48 | 49 | func CreateInstrumentMiddleware( 50 | requestCount metrics.Counter, 51 | requestLatency metrics.Histogram, 52 | countResult metrics.Histogram, 53 | ) StringMiddleware { 54 | return func(next StringService) StringService { 55 | return instrumentMiddleware{requestCount, requestLatency, countResult, next} 56 | } 57 | } 58 | 59 | func (mw instrumentMiddleware) Uppercase(s string) (output string, err error) { 60 | defer func(begin time.Time) { 61 | lvs := []string{"method", "uppercase", "error", fmt.Sprint(err != nil)} 62 | mw.requestCount.With(lvs...).Add(1) 63 | mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 64 | }(time.Now()) 65 | 66 | output, err = mw.StringService.Uppercase(s) 67 | return 68 | } 69 | 70 | func (mw instrumentMiddleware) Count(s string) (n int) { 71 | defer func(begin time.Time) { 72 | lvs := []string{"method", "count", "error", "false"} 73 | mw.requestCount.With(lvs...).Add(1) 74 | mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 75 | mw.countResult.Observe(float64(n)) 76 | }(time.Now()) 77 | 78 | n = mw.StringService.Count(s) 79 | return 80 | } 81 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Logging.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-kit/kit/log" 7 | ) 8 | 9 | type loggingMiddleware struct { 10 | logger log.Logger 11 | StringService 12 | } 13 | 14 | func CreateLoggingMiddleware(logger log.Logger) StringMiddleware { 15 | return func(next StringService) StringService { 16 | return loggingMiddleware{logger, next} 17 | } 18 | } 19 | 20 | func (mw loggingMiddleware) Uppercase(s string) (output string, err error) { 21 | defer func(begin time.Time) { 22 | mw.logger.Log( 23 | "method", "uppercase", 24 | "input", s, 25 | "output", output, 26 | "err", err, 27 | "took", time.Since(begin), 28 | ) 29 | }(time.Now()) 30 | 31 | output, err = mw.StringService.Uppercase(s) 32 | return 33 | } 34 | 35 | func (mw loggingMiddleware) Count(s string) (n int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "method", "count", 39 | "input", s, 40 | "n", n, 41 | "took", time.Since(begin), 42 | ) 43 | }(time.Now()) 44 | 45 | n = mw.StringService.Count(s) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Proxy.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | "time" 8 | 9 | "github.com/go-kit/kit/circuitbreaker" 10 | "github.com/go-kit/kit/endpoint" 11 | "github.com/go-kit/kit/log" 12 | "github.com/go-kit/kit/ratelimit" 13 | "github.com/go-kit/kit/sd" 14 | "github.com/go-kit/kit/sd/lb" 15 | jujuratelimit "github.com/juju/ratelimit" 16 | "github.com/pkg/errors" 17 | "github.com/sony/gobreaker" 18 | "golang.org/x/net/context" 19 | httptransport "github.com/go-kit/kit/transport/http" 20 | ) 21 | 22 | const loggingTag = "proxy_to" 23 | 24 | type proxyMiddleware struct { 25 | ctx context.Context 26 | StringService 27 | e endpoint.Endpoint 28 | } 29 | 30 | func (mw proxyMiddleware) Uppercase(s string) (string, error) { 31 | res, err := mw.e(mw.ctx, UppercaseRequest{s}) 32 | if err != nil { 33 | return "", errors.Wrap(err, "Failed to proxy UppercaseRequest") 34 | } 35 | 36 | resp := res.(UppercaseResponse) 37 | if resp.Err != "" { 38 | return resp.V, errors.New(resp.Err) 39 | } 40 | return resp.V, nil 41 | } 42 | 43 | func (mw proxyMiddleware) Count(s string) int { 44 | return mw.StringService.Count(s) 45 | } 46 | 47 | func CreateUppercaseProxyMiddleware( 48 | instances string, 49 | ctx context.Context, 50 | logger log.Logger) StringMiddleware { 51 | 52 | if instances == "" { 53 | logger.Log(loggingTag, "none") 54 | return func(next StringService) StringService { return next } 55 | } 56 | 57 | var ( 58 | maxRequest = 100 59 | maxRetry = 3 60 | maxTime = 250 * time.Millisecond 61 | ) 62 | 63 | var ( 64 | proxyList = parseProxyList(instances) 65 | subscriber sd.FixedSubscriber 66 | ) 67 | logger.Log("proxy_to", fmt.Sprint(proxyList)) 68 | for _, proxy := range proxyList { 69 | var e endpoint.Endpoint 70 | e = createUppercaseProxy(proxy) 71 | e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e) 72 | e = ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(float64(maxRequest), int64(maxRequest)))(e) 73 | subscriber = append(subscriber, e) 74 | } 75 | 76 | balancer := lb.NewRoundRobin(subscriber) 77 | retry := lb.Retry(maxRetry, maxTime, balancer) 78 | 79 | return func(next StringService) StringService { 80 | return proxyMiddleware{ctx, next, retry} 81 | } 82 | } 83 | 84 | func createUppercaseProxy(proxyURL string) endpoint.Endpoint { 85 | if !strings.HasPrefix(proxyURL, "http") { 86 | proxyURL = "http://" + proxyURL 87 | } 88 | u, err := url.Parse(proxyURL) 89 | if err != nil { 90 | err = errors.Wrap(err, "Failed to parse proxy URL") 91 | panic(err) 92 | } 93 | if u.Path == "" { 94 | u.Path = "/uppercase" 95 | } 96 | 97 | return httptransport.NewClient( 98 | "GET", 99 | u, 100 | EncodeRequest, 101 | DecodeUppercaseResponse, 102 | ).Endpoint() 103 | } 104 | 105 | func parseProxyList(s string) []string { 106 | ps := strings.Split(s, ",") 107 | for i := range ps { 108 | ps[i] = strings.TrimSpace(ps[i]) 109 | } 110 | 111 | return ps 112 | } 113 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | type StringService interface { 10 | Uppercase(string) (string, error) 11 | Count(string) int 12 | } 13 | 14 | type StringMiddleware func(StringService) StringService 15 | 16 | type StringServiceImpl struct{} 17 | 18 | func (StringServiceImpl) Uppercase(s string) (string, error) { 19 | if s == "" { 20 | return "", errors.New("Empty string") 21 | } 22 | 23 | return strings.ToUpper(s), nil 24 | } 25 | 26 | func (StringServiceImpl) Count(s string) int { 27 | return len(s) 28 | } 29 | 30 | type UppercaseRequest struct { 31 | S string `json:"s"` 32 | } 33 | 34 | type UppercaseResponse struct { 35 | V string `json:"v"` 36 | Err string `json:"err,omitempty"` 37 | } 38 | 39 | type CountRequest struct { 40 | S string `json:"s"` 41 | } 42 | 43 | type CountResponse struct { 44 | V int `json:"v"` 45 | } 46 | -------------------------------------------------------------------------------- /go-kit-tutorial/stringsvc3/service/Transport.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | 9 | "github.com/pkg/errors" 10 | "golang.org/x/net/context" 11 | ) 12 | 13 | func DecodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { 14 | var request UppercaseRequest 15 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 16 | return nil, errors.Wrap(err, "Failed to decode UppercaseRequest") 17 | } 18 | return request, nil 19 | } 20 | 21 | func DecodeUppercaseResponse(_ context.Context, r *http.Response) (interface{}, error) { 22 | var response UppercaseResponse 23 | if err := json.NewDecoder(r.Body).Decode(&response); err != nil { 24 | return nil, err 25 | } 26 | return response, nil 27 | } 28 | 29 | func DecodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) { 30 | var request CountRequest 31 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 32 | return nil, errors.Wrap(err, "Failed to decode CountRequest") 33 | } 34 | return request, nil 35 | } 36 | 37 | func EncodeRequest(_ context.Context, r *http.Request, request interface{}) error { 38 | var buf bytes.Buffer 39 | if err := json.NewEncoder(&buf).Encode(request); err != nil { 40 | return err 41 | } 42 | r.Body = ioutil.NopCloser(&buf) 43 | return nil 44 | } 45 | 46 | func EncodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { 47 | return json.NewEncoder(w).Encode(response) 48 | } 49 | -------------------------------------------------------------------------------- /go-tooling-in-action/email-server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | http.HandleFunc("/", handler) 14 | 15 | err := http.ListenAndServe(":8080", nil) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | } 20 | 21 | // enhanced version 22 | func handler(w http.ResponseWriter, r *http.Request) { 23 | path := r.URL.Path[1:] 24 | 25 | w.Header().Set("Content-Type", "text/plain") 26 | if strings.HasSuffix(path, "@golang.org") { 27 | name := strings.TrimSuffix(path, "@golang.org") 28 | fmt.Fprintf(w, "Hello gopher %s\n", name) 29 | return 30 | } 31 | 32 | fmt.Fprintf(w, "Hello dear %s\n", path) 33 | } 34 | 35 | var re = regexp.MustCompile("^(.+)@golang.org$") 36 | 37 | func handler2(w http.ResponseWriter, r *http.Request) { 38 | path := r.URL.Path[1:] 39 | match := re.FindAllStringSubmatch(path, -1) 40 | 41 | if match != nil { 42 | fmt.Fprintf(w, "Hello gopher %s\n", match[0][1]) 43 | return 44 | } 45 | 46 | fmt.Fprintf(w, "Hello dear %s\n", path) 47 | } 48 | -------------------------------------------------------------------------------- /go-tooling-in-action/email-server_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | var cases = []struct { 11 | in, out string 12 | }{ 13 | {"1ambda@golang.org", "gopher 1ambda"}, 14 | {"something", "dear something"}, 15 | } 16 | 17 | func TestHandler(t *testing.T) { 18 | 19 | for _, c := range cases { 20 | req, err := http.NewRequest( 21 | http.MethodGet, 22 | "http://localhost:8080/"+c.in, 23 | nil, 24 | ) 25 | 26 | if err != nil { 27 | t.Fatalf("could not create request %v", err) 28 | } 29 | 30 | rec := httptest.NewRecorder() 31 | handler(rec, req) 32 | 33 | if rec.Code != http.StatusOK { 34 | t.Errorf("expected 200; got %d", rec.Code) 35 | } 36 | 37 | if !strings.Contains(rec.Body.String(), c.out) { 38 | t.Errorf("unexpected body in response %q", rec.Body.String()) 39 | } 40 | } 41 | } 42 | 43 | func BenchmarkHandler(b *testing.B) { 44 | 45 | for i := 0; i < b.N; i++ { 46 | for _, c := range cases { 47 | req, err := http.NewRequest( 48 | http.MethodGet, 49 | "http://localhost:8080/"+c.in, 50 | nil, 51 | ) 52 | 53 | if err != nil { 54 | b.Fatalf("could not create request %v", err) 55 | } 56 | 57 | rec := httptest.NewRecorder() 58 | handler(rec, req) 59 | 60 | if rec.Code != http.StatusOK { 61 | b.Errorf("expected 200; got %d", rec.Code) 62 | } 63 | 64 | if !strings.Contains(rec.Body.String(), c.out) { 65 | b.Errorf("unexpected body in response %q", rec.Body.String()) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /golang-cheat-sheet/closure.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | test(closure1(1)() == 2) 5 | 6 | inner, two := closure2() 7 | test(inner() == 101) 8 | test(two == 2) 9 | 10 | test(closure3(1) == 3) 11 | } 12 | 13 | func closure1(n int) func() int { 14 | outer_var := 2 15 | 16 | // lexically scoped: 17 | // functions can access values that were in the same scoped when defining the function 18 | c := func() int { 19 | return outer_var + n 20 | } 21 | 22 | // but captured variable can be changed when original variable is changed 23 | outer_var = 1 24 | 25 | return c 26 | } 27 | 28 | func closure2() (func() int, int) { 29 | outer_var := 2 30 | 31 | // but original value doens't change even though the captured variable is changed 32 | inner := func() int { 33 | outer_var += 99 // this code do not change original `outer_var`, just redefining it 34 | return outer_var 35 | } 36 | 37 | return inner, outer_var // func, 2 38 | } 39 | 40 | func closure3(n int) int { 41 | outer_var := 2 42 | 43 | // change local (out of func) variable 44 | func() { 45 | outer_var = outer_var + n 46 | }() 47 | 48 | return outer_var 49 | } 50 | 51 | func test(expr bool) { 52 | if !expr { 53 | panic(0) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /golang-cheat-sheet/if-statement.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | // we can put one stmt before the condition 6 | b, c := 1, 3 7 | 8 | if a := b + c; a > 45 { 9 | panic(0) 10 | } 11 | 12 | // type assertion inside if 13 | var val interface{} 14 | val = "foo" 15 | 16 | if str, ok := val.(string); !ok { 17 | panic(str) 18 | } 19 | 20 | } 21 | 22 | func test(expr bool) { 23 | if !expr { 24 | panic(0) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /golang-cheat-sheet/map-usage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Vertex struct { 4 | a float64 5 | b float64 6 | } 7 | 8 | func main() { 9 | 10 | var m1 map[string]int = make(map[string]int) 11 | m1["key"] = 1 12 | 13 | if v1, ok := m1["key"]; ok { 14 | pass(true) 15 | _ = v1 16 | } 17 | 18 | // map literal 19 | var m2 = map[string]Vertex{ 20 | "Bell Labs": {40.68433, -74.39967}, 21 | "Google": {37.42202, -122.08408}, 22 | } 23 | _ = m2 24 | 25 | // using anonymous struct is cheaper and safer, 26 | // than using `map[string]interface{}` 27 | point := struct { 28 | X, Y int 29 | }{1, 2} 30 | _ = point 31 | } 32 | 33 | func pass(expr bool) { 34 | if !expr { 35 | panic(0) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /golang-cheat-sheet/switch-statement.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | // cases break automatically, no fallthrough by default 6 | switch os := getRuntimeOS(); os { 7 | case "darwin": 8 | pass(true) 9 | case "window": 10 | pass(false) 11 | case "linux": 12 | pass(false) 13 | default: 14 | pass(false) 15 | } 16 | } 17 | 18 | func pass(expr bool) { 19 | if !expr { 20 | panic(0) 21 | } 22 | } 23 | 24 | func getRuntimeOS() string { 25 | return "darwin" 26 | } 27 | -------------------------------------------------------------------------------- /interface/how-to-use-interfaces-in-go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | /** 12 | * If you write a function that takes an `interface{}` value as a param, 13 | * you can supply that function with any value. 14 | * 15 | * `func DoSomething(v interface{})` 16 | * 17 | * `v` is not any type. It is of `interface{}` type. 18 | * When passing a value into `DoSomething` function, 19 | * the Go runtime will perform a type conversino (if necessary), 20 | * and convert the value to an interface{} value. 21 | * 22 | * 23 | * All values have exactly one type at runtime, and v's static type is `interface{}` 24 | * 25 | * An interface value (passed to as a param) have two words of data. 26 | * - one word is used to point to a method table for the underlying type 27 | * - the other word is used to point the actual data being held by that value 28 | * 29 | * We can't convert `T[]` to `[]interface{}` for this reason. 30 | * They have different memory representations. 31 | */ 32 | 33 | names := []string{"stanley", "david", "oscar"} 34 | vals := make([]interface{}, len(names)) 35 | for i, v := range names { 36 | vals[i] = v 37 | } 38 | _ = vals 39 | 40 | /** 41 | * Pointer type can access to methods of value type while 42 | * value type can't access to methods of pointer type 43 | * 44 | * Usually, people don't use value receiver since 45 | * everyting passed as a param will be copied. 46 | */ 47 | 48 | animals := []Animal{&Dog{}, new(Cat), Llama{}, JavaProgrammer{}} 49 | 50 | for _, animal := range animals { 51 | _ = animal 52 | } 53 | 54 | // json parsing 55 | var val map[string]interface{} 56 | input := ` 57 | { 58 | "created_at": "Thu May 31 00:00:01 +0000 2012" 59 | } 60 | ` 61 | if err := json.Unmarshal([]byte(input), &val); err != nil { 62 | panic(err) 63 | } 64 | 65 | fmt.Println(val) 66 | for k, v := range val { 67 | fmt.Println(k, v, reflect.TypeOf(v)) 68 | } 69 | 70 | var val2 map[string]Timestamp 71 | 72 | if err := json.Unmarshal([]byte(input), &val2); err != nil { 73 | panic(err) 74 | } 75 | 76 | fmt.Println(val2) 77 | for k, v := range val2 { 78 | fmt.Println(k, reflect.TypeOf(v)) 79 | } 80 | fmt.Println(time.Time(val2["created_at"])) 81 | } 82 | 83 | type Animal interface { 84 | Speak() string 85 | } 86 | 87 | type Dog struct{} 88 | 89 | func (d Dog) Speak() string { 90 | return "Woof!" 91 | } 92 | 93 | type Cat struct{} 94 | 95 | func (c *Cat) Speak() string { 96 | return "Meow!" 97 | } 98 | 99 | type Llama struct{} 100 | 101 | func (l Llama) Speak() string { 102 | return "????" 103 | } 104 | 105 | type JavaProgrammer struct{} 106 | 107 | func (j JavaProgrammer) Speak() string { 108 | return "Design patterns!" 109 | } 110 | 111 | type Timestamp time.Time 112 | 113 | func (t *Timestamp) UnmarshalJSON(b []byte) error { 114 | v, err := time.Parse(time.RubyDate, string(b[1:len(b)-1])) 115 | if err != nil { 116 | return err 117 | } 118 | *t = Timestamp(v) 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /json/json-interfaces-and-go-generate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type ShirtSize byte 12 | 13 | const ( 14 | NA ShirtSize = iota 15 | XS 16 | S 17 | M 18 | L 19 | XL 20 | ) 21 | 22 | func main() { 23 | raw := ` 24 | { 25 | "name": "Gopher", 26 | "birthdate": "2009/11/10", 27 | "shirt-size": "XS" 28 | } 29 | ` 30 | 31 | // 1. initial version 32 | var p1 Person1 33 | if err := p1.Parse(raw); err != nil { 34 | panic("p1.Parse failed") 35 | } 36 | 37 | // 2. use aux struct 38 | var p2 Person2 39 | if err := p2.Parse(raw); err != nil { 40 | panic("p2.Parse failed") 41 | } 42 | 43 | // 3. use Marshaler and Unmarshaler 44 | var p3 Person3 45 | dec := json.NewDecoder(strings.NewReader(raw)) 46 | if err := dec.Decode(&p3); err != nil { 47 | log.Fatal("p3.Parse failed", err) 48 | panic(err) 49 | } 50 | } 51 | 52 | type Date struct{ time.Time } 53 | 54 | type Person3 struct { 55 | Name string `json:"name"` 56 | Born Date `json:"birthdate"` 57 | Size ShirtSize `json:"shirt-size"` 58 | } 59 | 60 | func (d *Date) UnmarshalJSON(data []byte) error { 61 | var s string 62 | if err := json.Unmarshal(data, &s); err != nil { 63 | return fmt.Errorf("birthdate should be a string, got %s", data) 64 | } 65 | 66 | t, err := time.Parse("2006/01/02", s) 67 | if err != nil { 68 | return fmt.Errorf("invalid date: %v", err) 69 | } 70 | 71 | d.Time = t 72 | return nil 73 | } 74 | 75 | func (ss *ShirtSize) UnmarshalJSON(data []byte) error { 76 | var s string 77 | if err := json.Unmarshal(data, &s); err != nil { 78 | return fmt.Errorf("shirt-size should be a string, got %s", data) 79 | } 80 | 81 | got, ok := map[string]ShirtSize{"XS": XS, "S": S, "M": M, "L": L, "XL": XL}[s] 82 | if !ok { 83 | return fmt.Errorf("invalid ShirtSize: %q", s) 84 | } 85 | 86 | *ss = got 87 | return nil 88 | } 89 | 90 | type Person2 struct { 91 | Name string `json:"name"` 92 | Born time.Time `json:"birthdate"` 93 | Size ShirtSize `json:"shirt-size"` 94 | } 95 | 96 | func (p *Person2) Parse(s string) error { 97 | // Let's use an auxiliary struct type to avoid parsing 98 | // Note: the field tag for Name is not needed; 99 | // the JSON decoder perform a case insensitive match 100 | // if the exact form is not found. 101 | var aux struct { 102 | Name string 103 | Born string `json:"birthdate"` 104 | Size string `json:"shirt-size"` 105 | } 106 | 107 | dec := json.NewDecoder(strings.NewReader(s)) 108 | if err := dec.Decode(&aux); err != nil { 109 | return fmt.Errorf("decode person: %v", err) 110 | } 111 | 112 | p.Name = aux.Name 113 | born, err := time.Parse("2006/01/02", aux.Born) 114 | if err != nil { 115 | return fmt.Errorf("invalid date: %v", err) 116 | } 117 | p.Born = born 118 | p.Size, err = ParseShirtSize(aux.Size) 119 | return err 120 | } 121 | 122 | type Person1 struct { 123 | Name string 124 | Born time.Time 125 | Size ShirtSize 126 | } 127 | 128 | func (p *Person1) Parse(s string) error { 129 | // use map 130 | fields := map[string]string{} 131 | 132 | dec := json.NewDecoder(strings.NewReader(s)) 133 | if err := dec.Decode(&fields); err != nil { 134 | return fmt.Errorf("decode person: %v", err) 135 | } 136 | 137 | // 1. get name 138 | p.Name = fields["name"] 139 | 140 | // 2. get birthdate 141 | born, err := time.Parse("2006/01/02", fields["birthdate"]) 142 | if err != nil { 143 | return fmt.Errorf("invalid date %v", err) 144 | } 145 | p.Born = born 146 | 147 | // 3. get shirt size 148 | p.Size, err = ParseShirtSize(fields["shirt-size"]) 149 | 150 | return nil 151 | } 152 | 153 | func ParseShirtSize(s string) (ShirtSize, error) { 154 | sizes := map[string]ShirtSize{"XS": XS, "S": S, "M": M, "L": L, "XL": XL} 155 | ss, ok := sizes[s] 156 | if !ok { 157 | return NA, fmt.Errorf("invalid ShirtSize %q", s) 158 | } 159 | return ss, nil 160 | } 161 | -------------------------------------------------------------------------------- /json/roman-numerals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "strings" 8 | 9 | roman "github.com/StefanSchroeder/Golang-Roman" 10 | ) 11 | 12 | type RomanNumeral int 13 | type Movie struct { 14 | Title string 15 | Year RomanNumeral 16 | } 17 | 18 | func main() { 19 | 20 | // Encoding 21 | movies := []Movie{{"E.T.", 1982}, {"The Matrix", 1999}} 22 | encoded, err := json.MarshalIndent(movies, "", "\t") 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | fmt.Printf("Movies: %s\n", encoded) 27 | 28 | // Decoding 29 | var m Movie 30 | input := `{"Title": "Alien", "Year": "MCMLXXIX"}` 31 | if err := json.NewDecoder(strings.NewReader(input)).Decode(&m); err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | fmt.Printf("%s was released in %d\n", m.Title, m.Year) 36 | } 37 | 38 | func (r *RomanNumeral) UnmarshalJSON(data []byte) error { 39 | var s string 40 | if err := json.Unmarshal(data, &s); err != nil { 41 | return fmt.Errorf("roman numerals should be string but got %s", data) 42 | } 43 | 44 | res := roman.Arabic(s) 45 | if res == -1 { 46 | return fmt.Errorf("invalid roman numerals: %s", data) 47 | } 48 | 49 | *r = RomanNumeral(res) 50 | return nil 51 | } 52 | 53 | func (r RomanNumeral) MarshalJSON() ([]byte, error) { 54 | if r <= 0 { 55 | return nil, fmt.Errorf("Roman had only natural numbers, but got %d", r) 56 | } 57 | 58 | s := roman.Roman(int(r)) 59 | return json.Marshal(s) 60 | } 61 | -------------------------------------------------------------------------------- /just-for-func/027_two-ways-of-merging-N-channels/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"time" 5 | //"math/rand" 6 | "fmt" 7 | "sync" 8 | "reflect" 9 | ) 10 | 11 | func main() { 12 | a := asChan(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 13 | b := asChan(10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 14 | c := asChan(20, 21, 22, 23, 24, 25, 26, 27, 28, 29) 15 | 16 | for v := range mergeReflect(a, b, c) { 17 | fmt.Println(v) 18 | } 19 | } 20 | 21 | // slow 22 | func merge1(chans ...<-chan int) <-chan int { 23 | out := make(chan int) 24 | 25 | go func() { 26 | defer close(out) 27 | 28 | for _, c := range chans { 29 | // we can't use select stmt since the number of channels is random 30 | // also, we need to pass `c` instead of using local `c` which is changes in parent goroutine 31 | for v := range c { 32 | out <- v 33 | } 34 | } 35 | }() 36 | 37 | return out 38 | } 39 | 40 | // more concurrent 41 | func merge(chans ...<-chan int) <-chan int { 42 | out := make(chan int) 43 | 44 | go func() { 45 | defer close(out) 46 | 47 | var wg sync.WaitGroup 48 | wg.Add(len(chans)) 49 | 50 | for _, c := range chans { 51 | // we can't use select stmt since the number of channels is random 52 | // also, we need to pass `c` instead of using local `c` which is changes in parent goroutine 53 | go func(c <-chan int) { 54 | for v := range c { 55 | out <- v 56 | } 57 | 58 | wg.Done() 59 | }(c) 60 | } 61 | 62 | wg.Wait() 63 | }() 64 | 65 | return out 66 | } 67 | 68 | func mergeReflect(chans ...<-chan int) <-chan int { 69 | out := make(chan int) 70 | 71 | go func() { 72 | defer close(out) 73 | 74 | var cases []reflect.SelectCase 75 | for _, c := range chans { 76 | cases = append(cases, reflect.SelectCase{ 77 | Dir: reflect.SelectRecv, 78 | Chan: reflect.ValueOf(c), 79 | }) 80 | } 81 | 82 | for len(cases) > 0 { 83 | i, v, ok := reflect.Select(cases) 84 | if !ok { 85 | // remove `i` from slice 86 | cases = append(cases[:i], cases[i+1:]...) 87 | continue 88 | } 89 | 90 | out <- v.Interface().(int) 91 | } 92 | }() 93 | 94 | return out 95 | } 96 | 97 | func asChan(vs ...int) <-chan int { 98 | c := make(chan int) 99 | 100 | go func() { 101 | defer close(c) 102 | 103 | for _, v := range vs { 104 | c <- v 105 | //time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) 106 | } 107 | }() 108 | 109 | return c 110 | } 111 | 112 | func mergeRecur(chans ...<-chan int) <-chan int { 113 | switch len(chans) { 114 | case 0: 115 | c := make(chan int) 116 | close(c) 117 | return c 118 | case 1: 119 | return chans[0] 120 | case 2: 121 | return mergeTwo(chans[0], chans[1]) 122 | default: 123 | m := len(chans) / 2 124 | return mergeTwo(mergeRecur(chans[:m]...), mergeRecur(chans[m:]...)) 125 | } 126 | } 127 | 128 | func mergeTwo(a, b <-chan int) <-chan int { 129 | out := make(chan int) 130 | 131 | go func() { 132 | defer close(out) 133 | 134 | for a != nil || b != nil { 135 | select { 136 | case v, ok := <-a: 137 | if !ok { 138 | a = nil 139 | continue 140 | } 141 | out <- v 142 | 143 | case v, ok := <-b: 144 | if !ok { 145 | b = nil 146 | continue 147 | } 148 | out <- v 149 | } 150 | } 151 | }() 152 | 153 | return out 154 | } 155 | -------------------------------------------------------------------------------- /just-for-func/027_two-ways-of-merging-N-channels/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | var funcs = []struct { 9 | name string 10 | f func(...<-chan int) <-chan int 11 | }{ 12 | {"goroutines", merge,}, 13 | {"reflection", mergeReflect,}, 14 | {"recursion", mergeRecur,}, 15 | } 16 | 17 | func TestMerge(t *testing.T) { 18 | for _, f := range funcs { 19 | t.Run(f.name, func(t *testing.T) { 20 | c := f.f(asChan(1, 2, 3), asChan(4, 5, 6), asChan(7, 8, 9)) 21 | 22 | seen := make(map[int]bool) 23 | 24 | for v := range c { 25 | if seen[v] { 26 | t.Errorf("saw %d at least twice", v) 27 | } 28 | 29 | seen[v] = true 30 | } 31 | 32 | for i := 1; i <= 9; i++ { 33 | if !seen[i] { 34 | t.Errorf("didn't see %d", i) 35 | } 36 | } 37 | }) 38 | } 39 | } 40 | 41 | func BenchmarkMerge(b *testing.B) { 42 | for _, f := range funcs { 43 | for n := 1; n <= 1024; n *= 2 { 44 | 45 | chans := make([]<-chan int, n) 46 | 47 | b.Run(fmt.Sprintf("%s/%d", f.name, n), func(b *testing.B) { 48 | for i := 0; i < b.N; i++ { 49 | for j := range chans { 50 | chans[j] = asChan(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 51 | } 52 | 53 | c := f.f(chans...) 54 | 55 | for range c { 56 | 57 | } 58 | } 59 | }) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /just-for-func/README.md: -------------------------------------------------------------------------------- 1 | # just-for-func 2 | 3 | - [Youtube: Just For Func](https://www.youtube.com/channel/UC_BzFbxG2za3bp5NRRRXJSw/videos)) 4 | 5 | 6 | ## TOC 7 | 8 | - [#27 Two ways of merging N channels](https://www.youtube.com/watch?v=B64hIRjNvLc&t=1s) -------------------------------------------------------------------------------- /map/map-in-action.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "fmt" 5 | "sort" 6 | "sync" 7 | ) 8 | 9 | type Key struct { 10 | Path, Country string 11 | } 12 | 13 | // Ref: https://blog.golang.org/go-maps-in-action 14 | func main() { 15 | /** 16 | * Map types are reference types, like pointers or slices, and 17 | * so the value of `m` below is `nil` It doens't point to an initialized map. 18 | * Attemping to write to a nil map will cause runtime panic. 19 | * 20 | * The `make` function allocates and initializes a hash map and 21 | * returns map value that points to it. 22 | */ 23 | 24 | var m map[string]string 25 | m = make(map[string]string) 26 | m["route"] = "1" 27 | test(m["route"] == "1") 28 | 29 | n := len(m) 30 | test(n == 1) 31 | 32 | // The `delete` function doesn't return anything, and will do othing 33 | // If the specified key doesn't exist 34 | delete(m, "route") 35 | _, ok := m["route"] 36 | test(ok == false) 37 | 38 | m["root"] = "admin" 39 | 40 | for key, value := range m { 41 | test(key == "root") 42 | test(value == "admin") 43 | } 44 | 45 | // Initializing a map with data 46 | commits := map[string]int{ 47 | "rsc": 3711, 48 | "r": 2138, 49 | "gri": 1908, 50 | "adg": 912, 51 | } 52 | test(len(commits) == 4) 53 | 54 | empty := map[string]int{} 55 | test(len(empty) == 0) 56 | 57 | // Language spec defines the possible keys for map, but in short, comparable types are 58 | // boolean, numeric, string, pointer, channel, interface types, and structs or arrays 59 | // Not slice, maps, functions! these types can not be compared using `==` 60 | // But structs can be used as the key 61 | 62 | hits := make(map[string]map[string]int) 63 | test(hits["/doc/"]["au"] == 0) 64 | 65 | // We need to create an map for unvisited url 66 | add := func(m map[string]map[string]int, path, country string) { 67 | mm, ok := m[path] 68 | if !ok { 69 | mm = make(map[string]int) 70 | m[path] = mm 71 | } 72 | 73 | mm[country]++ 74 | } 75 | 76 | add(hits, "/doc/", "au") 77 | test(hits["/doc/"]["au"] == 1) 78 | 79 | // On the other hand, a design that uses a single map with a struct key can help us! 80 | 81 | hits2 := make(map[Key]int) 82 | hits2[Key{"/", "vn"}]++ 83 | test(hits2[Key{"/ref/spec", "ch"}] == 0) 84 | 85 | // Important: 86 | // Maps are not safe for concurrent use: https://golang.org/doc/faq#atomic_maps 87 | 88 | var counter = struct { 89 | sync.RWMutex 90 | m map[string]int 91 | }{m: make(map[string]int)} 92 | 93 | counter.RLock() 94 | count := counter.m["some_key"] 95 | counter.RUnlock() 96 | test(count == 0) 97 | 98 | // Important: 99 | // The iteration order is not specified and is not guaranteed to be the same 100 | 101 | var unordered map[int]string 102 | var keys []int 103 | for k := range unordered { 104 | keys = append(keys, k) 105 | } 106 | sort.Ints(keys) 107 | test(len(unordered) == 0) 108 | } 109 | 110 | func test(expr bool) { 111 | if !expr { 112 | panic(0) 113 | } 114 | } 115 | 116 | func use(vals ...interface{}) { 117 | for _, val := range vals { 118 | _ = val 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /pointer/things-i-wish-someone-had-told-me-about-go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func test(expr bool) { 4 | if !expr { 5 | panic("failed") 6 | } 7 | } 8 | 9 | func main() { 10 | 11 | // Everything passed as parameter will be copied in Golang 12 | u1 := &User{Name: "Leto"} 13 | test(u1.Name == "Leto") 14 | 15 | Modify1(u1, "Paul") 16 | test(u1.Name == "Leto") 17 | 18 | u2 := User{Name: "Leto"} 19 | Modify2(u2, "Duncan") 20 | test(u2.Name == "Leto") 21 | 22 | u3 := &User{Name: "Leto"} 23 | Modify3(u3, "Paul") 24 | test(u3.Name == "Paul") 25 | 26 | /** 27 | * func(ServeHTTP res http.ResponseWriter, req *http.Request) { ... } 28 | */ 29 | 30 | // Technically, we don't know whether the value being passed is 31 | // a copy of pointer / a copy of value 32 | // but it's probably the former 33 | 34 | StoreCache(&CachedUser{Name: "Leto", id: "leto"}) 35 | StoreCache(Application{id: "letoApp"}) 36 | } 37 | 38 | type User struct { 39 | Name string 40 | } 41 | 42 | func Modify1(u *User, name string) { 43 | u = &User{Name: name} 44 | } 45 | 46 | func Modify2(u User, name string) { 47 | u.Name = name 48 | } 49 | 50 | func Modify3(u *User, name string) { 51 | u.Name = name 52 | } 53 | 54 | type CacheItem interface { 55 | GetId() string 56 | } 57 | 58 | func StoreCache(item CacheItem) { 59 | // do something iwth item.GetId() 60 | } 61 | 62 | type Application struct { 63 | id string 64 | } 65 | 66 | type CachedUser struct { 67 | id string 68 | Name string 69 | } 70 | 71 | func (a Application) GetId() string { 72 | return a.id 73 | } 74 | 75 | func (u *CachedUser) GetId() string { 76 | return u.id 77 | } 78 | -------------------------------------------------------------------------------- /slice/slice-usage-and-internals_test.go: -------------------------------------------------------------------------------- 1 | package slice_test 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ExampleSlice() { 8 | /** 9 | * Go's arrays are values. 10 | * An array variable denotes the entire array, not a pointer to the first element. 11 | * This means that when you assign or pass around an array, 12 | * you will make a copy of its content 13 | * To avoid the copy, you could pass a pointer to array. 14 | */ 15 | initialized1 := [2]string{"Penn", "Teller"} 16 | initialized2 := [...]string{"Penn", "Teller"} 17 | use(initialized1, initialized2) 18 | 19 | // Output: 20 | 21 | // Example: initialize slice 22 | letters := []string{"a", "b", "c", "d"} 23 | use(letters) 24 | 25 | var bytes []byte = make([]byte, 5) 26 | test(len(bytes), cap(bytes)) 27 | // 5 5 28 | 29 | // Example: slicing 30 | b := []byte{'g', 'o', 'l', 'a', 'n', 'g'} 31 | // Comment: `b[1:4]` will share the same storage as b 32 | test(b[1:4]) 33 | // "ola" 34 | test(b[:]) 35 | // "golang" 36 | test(b[:2]) 37 | // "go" 38 | test(b[2:]) 39 | // "lang" 40 | 41 | /** 42 | * A slice is a descriptor of an array segment. 43 | * It soncicst of a 44 | * - pointer to the array 45 | * - the length of the segment 46 | * - its capacity 47 | * 48 | * Slicing doesn't copy the slice's data. 49 | * It just creates a newslice value that points to the original array. 50 | * Therefore, modifying the elements of a re-slice modifies the elements of the original 51 | */ 52 | 53 | road := []byte{'r', 'o', 'a', 'd'} 54 | road2 := road[2:] 55 | test(road2) 56 | // "ad" 57 | road2[1] = 'm' 58 | test(road2) 59 | // "am" 60 | test(road) 61 | // "roam" 62 | 63 | /** 64 | * We can grow a shrinked slice to its capacity by slicing it again. 65 | * `s = s[:cap(s)]` 66 | * 67 | * But a slice cannot be grown beyond its capacity. 68 | * Attemping to do so will cause a runtime panic, 69 | * just as when indexing outside the bounds of a slice or array 70 | */ 71 | 72 | /** 73 | * To increase the cap of a slice, one must create a new, larger slice 74 | * and copy the contents of the original slice into it. 75 | */ 76 | 77 | gopher := []string{"g", "o", "p", "h", "e", "r"} 78 | gopher2 := make([]string, len(gopher), (cap(gopher)+1)*2) 79 | for i := range gopher { 80 | gopher2[i] = gopher[i] 81 | } 82 | test(gopher2) 83 | // "gopher" 84 | test(cap(gopher2)) 85 | // 12 86 | 87 | // Comment: we can use `copy` function instead of looping each element 88 | gopher3 := make([]string, len(gopher), (cap(gopher)+1)*2) 89 | copy(gopher3, gopher) 90 | test(cap(gopher2) == cap(gopher3)) 91 | // True 92 | 93 | /** 94 | * The `append` func appends elements to the end of a slice, 95 | * and grows the slice if a greater cap is needed 96 | */ 97 | 98 | gopher4 := append(gopher, "gopher") 99 | test(cap(gopher4) == 12) 100 | // True 101 | 102 | // Comment: we can splat operator to append slice into slice 103 | names1 := []string{"John", "Paul"} 104 | names2 := []string{"George", "Ringo", "Pete"} 105 | names3 := append(names1, names2...) 106 | test(len(names3)) 107 | // 5 108 | 109 | // See possible gotcha from here 110 | // https://blog.golang.org/go-slices-usage-and-internals#TOC_6. 111 | // Summary: Copy only needed elements before return 112 | // otherwise, golang runtime will keep all data 113 | } 114 | 115 | func test(vals ...interface{}) { 116 | fmt.Printf("%q", vals) 117 | } 118 | 119 | func use(vals ...interface{}) { 120 | for _, val := range vals { 121 | _ = val 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /understanding-nil/make-zero-value-useful.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | // make zero value useful 11 | 12 | // 1. pointers: we can call even `nil` receiver! 13 | var t *tree 14 | equal(t.sum() == 0) 15 | 16 | // 2. slices 17 | var s []int 18 | for i := range s { //interates zero times 19 | _ = i 20 | panic("won't be executed") 21 | } 22 | 23 | for i := 0; i < 10; i++ { 24 | // append on nil slices 25 | s = append(s, i) // fast enought since slice increase its cap twice if needed 26 | } 27 | 28 | // IMPORTANT: s[0] will cause panic 29 | 30 | // 3. maps 31 | var headers map[string]string 32 | equal(len(headers) == 0) 33 | 34 | for k, v := range headers { // iterates zero times 35 | _, _ = k, v 36 | panic("won't be executed") 37 | } 38 | headerValue, headerExist := headers["Accept"] 39 | equal(headerValue == "") 40 | equal(headerExist == false) 41 | 42 | // IMPORTANT: headers["abc"] = "def" will cause panic 43 | 44 | // good example making use of nil map 45 | newGet := func(url string, headers map[string]string) (*http.Request, error) { 46 | req, err := http.NewRequest(http.MethodGet, url, nil) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | for k, v := range headers { 52 | req.Header.Set(k, v) 53 | } 54 | 55 | return req, nil 56 | } 57 | _ = newGet 58 | // we can pass `nil` instead of `map[string]string{}` 59 | // IMPORTANT: Use `nil` maps as read-only empty maps 60 | 61 | // 4. channels 62 | var c1 chan int 63 | equal(c1 == nil) 64 | // IMPORTANT: `<- c` blocks forever (reading nil channel) 65 | // IMPORTANT: `c <- x` blocks forever (writing to nil channel) 66 | // IMPORTANT: `close(c)` causes panic (closing nil channel) 67 | 68 | // we can check channnel is closed or not 69 | c2 := make(chan int) 70 | close(c2) 71 | cv, ok := <-c2 72 | equal(ok == false) 73 | _ = cv 74 | 75 | // IMPORTANT: use nil chans to disable a select case 76 | // see merge1, merge2, merge3 77 | 78 | // 5. functions: nil funcs for default value 79 | newServerFunc := func(logger func(string, ...interface{})) { 80 | if logger == nil { 81 | logger = log.Printf // nil can also imply default behavior 82 | } 83 | } 84 | _ = newServerFunc 85 | 86 | // 6. interfaces: The nil interface is used as a signal for `default`` for the interface 87 | 88 | doSum := func(s Summer) int { 89 | if s == nil { 90 | return 0 91 | } 92 | return s.sum() 93 | } 94 | 95 | // (*tree, nil) and default behavior of `*tree` 96 | var tr *tree 97 | equal(doSum(tr) == 0) 98 | // (ints, nil) and default behavior of `ints` 99 | var is ints 100 | equal(doSum(is) == 0) 101 | // (nil, nil) which implies we need really `default behavior` for `Summer` 102 | equal(doSum(nil) == 0) 103 | 104 | } 105 | 106 | func equal(expr bool) { 107 | if !expr { 108 | panic("error") 109 | } 110 | } 111 | 112 | type Summer interface { 113 | sum() int 114 | } 115 | 116 | type ints []int 117 | 118 | func (i ints) sum() int { 119 | s := 0 120 | for _, v := range i { 121 | s += v 122 | } 123 | 124 | return s 125 | } 126 | 127 | func merge3(out chan<- int, a, b <-chan int) { 128 | for a != nil || b != nil { 129 | select { 130 | case v, ok := <-a: 131 | if !ok { 132 | a = nil 133 | continue 134 | } 135 | out <- v 136 | case v, ok := <-b: 137 | if !ok { 138 | b = nil 139 | continue 140 | } 141 | out <- v 142 | } 143 | } 144 | 145 | close(out) 146 | } 147 | 148 | func merge2(out chan<- int, a, b <-chan int) { 149 | var aClosed, bClosed bool 150 | 151 | for !aClosed || !bClosed { 152 | select { 153 | case v, ok := <-a: // PROBLEM: will cause busy loop 154 | if !ok { 155 | aClosed = true 156 | continue 157 | } 158 | out <- v 159 | case v, ok := <-b: // PROBLEM: will cause busy loop 160 | if !ok { 161 | bClosed = true 162 | continue 163 | } 164 | out <- v 165 | } 166 | } 167 | 168 | // without closing `out`, this blocks forever (deadlock) 169 | close(out) 170 | } 171 | 172 | // PROBLEM: run forever if `out` is closed 173 | func merge1(out chan<- int, a, b <-chan int) { 174 | for { 175 | select { 176 | case v := <-a: 177 | out <- v 178 | case v := <-b: 179 | out <- v 180 | } 181 | } 182 | } 183 | 184 | type tree struct { 185 | v int 186 | l *tree 187 | r *tree 188 | } 189 | 190 | // nil receivers are useful 191 | func (t *tree) sum() int { 192 | if t == nil { 193 | return 0 194 | } 195 | 196 | return t.v + t.l.sum() + t.r.sum() 197 | } 198 | 199 | func (t *tree) String() string { 200 | if t == nil { 201 | return "" 202 | } 203 | 204 | return fmt.Sprint(t.l, t.v, t.r) 205 | } 206 | 207 | func (t *tree) Find(v int) bool { 208 | if t == nil { 209 | return false 210 | } 211 | 212 | return t.v == v || t.l.Find(v) || t.r.Find(v) 213 | } 214 | 215 | // problem of sum1 216 | // - code repretition: `if v != nil...` 217 | // - panic when `t` is nil 218 | func (t *tree) sum1() int { 219 | sum := t.v 220 | 221 | if t.l != nil { 222 | sum += t.l.sum1() 223 | } 224 | 225 | if t.r != nil { 226 | sum += t.r.sum1() 227 | } 228 | 229 | return sum 230 | } 231 | -------------------------------------------------------------------------------- /understanding-nil/understanding-nil.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // pointers, slices, maps, channels, functions and interfaces 7 | // these types have `nil` as the zero value 8 | 9 | // nil slice has `nil` array 10 | var slice1 []byte 11 | equal(len(slice1) == 0) 12 | equal(cap(slice1) == 0) 13 | equal(slice1 == nil) 14 | 15 | // channels, maps, and functions are actually pointing their implementation 16 | var map1 map[string]int 17 | equal(map1 == nil) 18 | var func1 func() 19 | equal(func1 == nil) 20 | var chan1 chan int 21 | equal(chan1 == nil) 22 | 23 | // every interface has its underlying (type, value) 24 | var interface1 fmt.Stringer // Stringer (nil, nil) 25 | equal(interface1 == nil) // (nil, nil) equals nil 26 | 27 | // IMPORTANT: but (*Person, nil) is not nil 28 | var p1 *Person 29 | var interface2 fmt.Stringer = p1 30 | equal(interface2 != nil) 31 | 32 | // IMPORTANT: do not declare concrete error 33 | err := wrapGetMyError() 34 | equal(err != nil) 35 | } 36 | 37 | func equal(expr bool) { 38 | if !expr { 39 | panic("error") 40 | } 41 | } 42 | 43 | type Person struct { 44 | Name string 45 | Age int 46 | } 47 | 48 | func (p Person) String() string { 49 | return fmt.Sprintf("%s %d", p.Name, p.Age) 50 | } 51 | 52 | type MyError struct{} 53 | 54 | func (e MyError) Error() string { 55 | return "MyError :(" 56 | } 57 | 58 | func getMyError() *MyError { 59 | return nil 60 | } 61 | 62 | func wrapGetMyError() error { 63 | return getMyError() // return `error (*MyError, nil)`. this is not nil! 64 | } 65 | --------------------------------------------------------------------------------