├── .gitignore ├── 01-implement-interface-fast └── main.go ├── 02-variadic-functions-with-silce └── main.go ├── 03-cast-interface └── main.go ├── 04-named-return └── main.go ├── 05-options-pattern ├── config_way.go ├── main.go └── options_way.go ├── 06-range-expression └── main.go ├── 07-map-trap ├── main.go └── map_struct.go.fake ├── 08-map-struct-update └── main.go ├── 09-no-class-Inheritance └── main.go ├── 10-interface-compare ├── detail.go └── main.go ├── 11-variable-promotion ├── 11-variable-promotion └── main.go ├── 12-receiver-choose ├── addressable.go └── main.go ├── 13-why-map-iteration-random └── main.go ├── 14-kill-if ├── main.go └── responsibility_chain.go ├── 15-table-drive-test ├── main.go └── main_test.go ├── 16-fuzzy-test ├── main.go └── main_test.go ├── 17-guard-clauses └── main.go ├── 18-di-with-wire ├── main.go ├── wire.go └── wire_gen.go ├── 19-json-struct-tip ├── tip0.go ├── tip1.go ├── tip2.go ├── tip3.go └── tip4.go ├── 20-json-track └── main.go ├── 21-functional-dao ├── go.mod ├── go.sum ├── main.go └── model │ ├── common.go │ ├── mode_config.go │ ├── model_functional.go │ └── model_raw.go ├── 22-timewheel ├── main.go └── txt │ ├── raw.go │ └── tw.go ├── 23-kill-err ├── killer │ ├── nil-style.go │ └── struct-style.go └── main.go ├── 24-error-stack ├── errorSimple │ ├── better-error.go │ └── raw.go └── main.go ├── 25-refactor-extract ├── extract.go └── raw.go ├── go.mod ├── go.sum └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | 00-playground -------------------------------------------------------------------------------- /01-implement-interface-fast/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type someInterface interface { 4 | DoSomething() 5 | DoAnotherThing() 6 | } 7 | 8 | type someStruct struct { 9 | } 10 | 11 | // 如何快速实现接口 - how to quick impl someInterface for someStruct 12 | 13 | // 1. imple someInterface for someStruct{} 14 | 15 | // 2. 16 | var _ someInterface = (*someStruct)(nil) 17 | 18 | // DoAnotherThing implements someInterface 19 | func (*someStruct) DoAnotherThing() { 20 | panic("unimplemented") 21 | } 22 | 23 | // DoSomething implements someInterface 24 | func (*someStruct) DoSomething() { 25 | panic("unimplemented") 26 | } 27 | -------------------------------------------------------------------------------- /02-variadic-functions-with-silce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func ShowName(book ...string) { 4 | for _, content := range book { 5 | println(content) 6 | } 7 | } 8 | 9 | func main() { 10 | ShowName("rust", "go", "cpp") 11 | } 12 | -------------------------------------------------------------------------------- /03-cast-interface/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type someStruct struct{} 4 | 5 | func NewPointer() *someStruct { 6 | var c *someStruct = nil 7 | return c 8 | } 9 | 10 | func main() { 11 | p := NewPointer() 12 | // 首 8 字节是否是 0 值 13 | if p == nil { 14 | println("p is nil") 15 | } else { 16 | println("p is not nil") 17 | } 18 | } 19 | 20 | /* 21 | # which output? 22 | # A. p is nil 23 | # B. p is not nil 24 | # C. panic 25 | */ 26 | 27 | /* 28 | 29 | interface 结构体定义 30 | 31 | type iface struct { 32 | tab *itab 33 | data unsafe.Pointer 34 | } 35 | 36 | interface 一个 16 个字节的结构体,首 8 字节是类型字段,后 8 字节是数据指针。 37 | 38 | interface 变量新创建的时候是 nil ,则这 16 个字节是全 0 值; 39 | 40 | interface 变量的 nil 判断,汇编逻辑是判断首 8 字节是否是 0 值 41 | 42 | 函数 NewPointer 中p是一个具体类型指针。所以,它一定会把接口变量 iface (返回的接口)前 8 字节设置非零字段的,因为有具体类型呀(无论具体类型是否是 nil 指针)。 43 | 44 | 具体类型到接口的赋值一定会导致接口非零 45 | */ 46 | 47 | /* 48 | In Go programming language, nil can refer to two things: a nil pointer or a nil interface value. 49 | 50 | A nil pointer is a special value of a pointer data type that indicates the pointer doesn't point to a valid memory address. When a pointer is declared but not initialized, it's zero value is nil. 51 | 52 | nil 指针占用的内存大小取决于指针数据类型,通常等于代码运行的平台上的内存地址的大小。 53 | 54 | nil 接口值由两部分组成:动态类型和动态值。动态类型部分是指向类型描述符的 nil 指针,该类型描述符描述了接口如果具有非 nil 值时存储的具体值的类型。动态值部分也是一个 nil 指针,表示没有具体值。因此,nil 接口值占用了两倍的内存地址大小。 55 | 56 | nil 指针和 nil 接口值的内存表示是不同的。前者占用的内存大小为一个内存地址的大小,而后者占用的内存大小为两个内存地址的大小。 57 | */ 58 | -------------------------------------------------------------------------------- /04-named-return/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | a := []string{"go", "rust", "cpp"} 7 | b := CopyIt(a) 8 | fmt.Println(b) 9 | } 10 | 11 | func CopyIt(raw []string) (r []string) { 12 | // r = append(r, raw...) 13 | copy(r, raw) 14 | return r 15 | } 16 | 17 | /* 18 | how to optimize function CopyIt 19 | target: one line code 20 | */ 21 | -------------------------------------------------------------------------------- /05-options-pattern/config_way.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Book__ struct { 4 | cfg *Config 5 | } 6 | type Config struct { 7 | Title string 8 | ISBN string 9 | } 10 | 11 | func NewBook__(cfg *Config) *Book__ { 12 | return &Book__{cfg} 13 | } 14 | 15 | func main__() { 16 | _ = NewBook__(&Config{ 17 | Title: "早点下班的go语言学习法", 18 | ISBN: "20230101", 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /05-options-pattern/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Book struct { 4 | Title string 5 | ISBN string 6 | } 7 | 8 | func NewBook(t string) *Book { 9 | return &Book{ 10 | Title: t, 11 | ISBN: "", 12 | } 13 | } 14 | 15 | func NewBook2(t string, i string) *Book { 16 | return &Book{ 17 | Title: t, 18 | ISBN: i, 19 | } 20 | } 21 | 22 | func main() { 23 | _ = NewBook("早点下班的go语言学习法") 24 | _ = NewBook2("早点下班的go语言学习法", "20230101") 25 | } 26 | 27 | // How To Make Constructor Better 28 | -------------------------------------------------------------------------------- /05-options-pattern/options_way.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Book___ struct { 4 | Title string 5 | Code string 6 | } 7 | type Option func(*Book___) 8 | 9 | func NewBook_(options ...Option) *Book___ { 10 | b := &Book___{} 11 | for _, option := range options { 12 | option(b) 13 | } 14 | return b 15 | } 16 | 17 | func WithTitle(title string) Option { 18 | return func(b *Book___) { 19 | b.Title = title 20 | } 21 | } 22 | 23 | func WithISBN(ISBN string) Option { 24 | return func(b *Book___) { 25 | b.Code = ISBN 26 | } 27 | } 28 | 29 | func main_() { 30 | _ = NewBook_( 31 | WithTitle("Go for Dummies"), WithISBN("1234567890"), 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /06-range-expression/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | s := []string{"something"} 7 | for i := 0; i < len(s); i++ { 8 | s = append(s, "new_one") 9 | 10 | fmt.Println(s) 11 | } 12 | } 13 | 14 | // Which Output is correct: 15 | // A. [something new_one] 16 | 17 | // B. [something new_one], 18 | // [something new_one new_one ] 19 | // [something new_one new_one new_one] ... 20 | 21 | // C. panic: runtime error: index out of range 22 | -------------------------------------------------------------------------------- /07-map-trap/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | m := map[string]int{ 7 | "rust": 1, 8 | "go": 2, 9 | "cpp": 3, 10 | } 11 | //copy one 12 | m_copy := make(map[string]int, len(m)) 13 | for k, v := range m { 14 | m_copy[k] = v 15 | } 16 | 17 | for k, v := range m_copy { 18 | m[k+"_new"] = v + 100 19 | } 20 | 21 | fmt.Printf("map length: %d\n", len(m)) 22 | } 23 | 24 | // Output 25 | // A: 3 26 | // B: 6 27 | // C: I don't know 28 | -------------------------------------------------------------------------------- /07-map-trap/map_struct.go.fake: -------------------------------------------------------------------------------- 1 | //fake code 2 | 3 | package main 4 | 5 | type Map struct { 6 | size int 7 | buckets []Bucket 8 | } 9 | type Bucket struct { 10 | key Key 11 | value Value 12 | } 13 | 14 | func (m *Map) Set(key Key, value Value) { 15 | // Compute the hash of the key. 16 | hash := hashFunction(key) 17 | // Determine the index of the bucket for this key. 18 | index := hash % m.size 19 | m.buckets[index] = Bucket{key: key, value: value} 20 | } 21 | 22 | func (m *Map) Get(key Key) (Value, bool) { 23 | hash := hashFunction(key) 24 | index := hash % m.size 25 | // Retrieve the value for the key from the bucket. 26 | if m.buckets[index].key == key { 27 | return m.buckets[index].value, true 28 | } else { 29 | return Value{}, false 30 | } 31 | } 32 | 33 | func hashFunction(key invalid type) { 34 | panic("unimplemented") 35 | } 36 | -------------------------------------------------------------------------------- /08-map-struct-update/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Book struct { 6 | Title string 7 | Prince int 8 | } 9 | 10 | func main() { 11 | m := make(map[string]*Book) 12 | m["rust"] = 13 | &Book{Title: "rust", Prince: 20} 14 | 15 | //how to update prince? 16 | // m["rust"].Prince = 30 17 | b, ok := m["rust"] 18 | if ok { 19 | b.Prince = 30 20 | m["rust"] = b 21 | 22 | } 23 | // _ = &m["rust"] 24 | 25 | fmt.Printf( 26 | "prince: %d", m["rust"].Prince) 27 | } 28 | -------------------------------------------------------------------------------- /09-no-class-Inheritance/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type People struct { 6 | name string 7 | } 8 | 9 | func (p *People) getName() string { 10 | return p.name 11 | } 12 | 13 | type Namer interface { 14 | getName() string 15 | } 16 | 17 | func sayHello(p Namer) { 18 | fmt.Printf("Hello %s", p.getName()) 19 | } 20 | 21 | type Boy struct { 22 | People 23 | } 24 | 25 | func main() { 26 | b := &Boy{ 27 | People: People{name: "John"}, 28 | } 29 | fmt.Printf("name is %s \n", b.name) 30 | fmt.Printf("hi to %s \n", b.getName()) 31 | 32 | sayHello(b) 33 | } 34 | -------------------------------------------------------------------------------- /10-interface-compare/detail.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "reflect" 4 | 5 | func main_() { 6 | var b = true 7 | var i interface{} = true 8 | 9 | if b == i { 10 | println("Comparable are equal") 11 | } 12 | 13 | if reflect.TypeOf(b) == reflect.TypeOf(i) { 14 | println("Dynamic types are equal") 15 | } 16 | 17 | if reflect.ValueOf(b) == reflect.ValueOf(i) { 18 | println("Dynamic values are equal") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /10-interface-compare/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | pool := []interface{}{true} 5 | _ = pool[0] 6 | /* 7 | if tag { 8 | fmt.Println("This is a boolean value") 9 | } 10 | */ 11 | 12 | /* 13 | //type assertion , get dynamic type of interface 14 | if tag.(bool) { 15 | fmt.Println("This is a boolean value") 16 | } 17 | */ 18 | 19 | /* 20 | //https://go.dev/ref/spec#Comparison_operators 21 | if tag == true { 22 | fmt.Println("This is a boolean value") 23 | } 24 | */ 25 | } 26 | -------------------------------------------------------------------------------- /11-variable-promotion/11-variable-promotion: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leizhenpeng/golang-trick/ba8ea88aca12363fb542d8625b17ab90547193ce/11-variable-promotion/11-variable-promotion -------------------------------------------------------------------------------- /11-variable-promotion/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func getCounter() func() int { 6 | count := 0 7 | return func() int { 8 | count++ 9 | return count 10 | } 11 | } 12 | 13 | func main() { 14 | counter := getCounter() 15 | fmt.Println(counter()) // 1 16 | fmt.Println(counter()) // 2 17 | fmt.Println(counter()) // 3 18 | } 19 | 20 | // 为何闭包会导致记忆效应? 21 | // 逃逸分析试试看 22 | // go build -gcflags='-m' 23 | -------------------------------------------------------------------------------- /12-receiver-choose/addressable.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type I interface{} 6 | type AInt int 7 | type AString string 8 | 9 | func explain1() { 10 | m := map[string]int{ 11 | "go": 2023, 12 | } 13 | var i I = m["go"] 14 | fmt.Printf("i is of type %T\n", i) 15 | } 16 | 17 | func explain2() { 18 | var a AInt = 5 19 | var aPtr *AInt 20 | 21 | var i I = a 22 | fmt.Printf("i is of type %T\n", i) 23 | // aPtr = &(i.(AInt)) 24 | var x AInt = i.(AInt) 25 | aPtr = &x 26 | 27 | var b AString = "hello" 28 | i = b 29 | fmt.Printf("i is of type %T, aPtr is of type %T\n", i, aPtr) 30 | } 31 | 32 | // can't take the address of value in interface 33 | // 1. some value is not addressable 34 | // 2. compiler will confilct with type 35 | -------------------------------------------------------------------------------- /12-receiver-choose/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Circle struct { 4 | Radius float64 5 | } 6 | 7 | type Shaper interface { 8 | Area() float64 9 | Scale(factor float64) 10 | } 11 | 12 | func (c *Circle) Area() float64 { 13 | 14 | return 3.14 * c.Radius * c.Radius 15 | } 16 | 17 | func (c *Circle) Scale(factor float64) { 18 | c.Radius = c.Radius * factor 19 | } 20 | 21 | func main_() { 22 | // Common Sense: 23 | // 1.Value methods can be invoked on pointers and values, 24 | // 2. pointer methods can only be invoked on pointers. 25 | // When the value is addressable, you can winvoking a pointer method on a value by inserting the address operator automatically. 26 | 27 | // var c Shaper = Circle{10} 28 | var c Circle = Circle{10} 29 | // c.Scale(2) 30 | (&c).Scale(2) 31 | // c.Area() 32 | (&c).Area() 33 | } 34 | -------------------------------------------------------------------------------- /13-why-map-iteration-random/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | m := map[int]string{ 7 | 1: "golang", 8 | 2: "rust", 9 | 3: "ts", 10 | } 11 | 12 | for k := range m { 13 | fmt.Println(k, m[k]) 14 | } 15 | 16 | } 17 | 18 | // why map iteration is random? 19 | // 1. 汇编 go build -gcflags="-l -S -N" ./tip1.go > initMap.s 2>&1 20 | // 2. 查看核心调用代码 mapiterinit 21 | // 3. 查看 mapiterinit 源代码 22 | -------------------------------------------------------------------------------- /14-kill-if/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type SellInfo struct { 4 | Price float64 5 | OrderCount int 6 | TotalCount int 7 | MemberShip int 8 | } 9 | 10 | func main2() { 11 | var a = SellInfo{ 12 | Price: 1.0, 13 | OrderCount: 1, 14 | TotalCount: 20, 15 | MemberShip: 1, 16 | } 17 | if a.Price > 0 { 18 | println("true") 19 | } 20 | if a.TotalCount > a.OrderCount { 21 | println("true") 22 | } else { 23 | println("false") 24 | } 25 | if a.MemberShip == 1 { 26 | println("true") 27 | } 28 | if a.Price < 100 && a.MemberShip == 2 { 29 | println("true") 30 | } 31 | // if ...else... 32 | // if ...else if ...else if ...else if ...else... 33 | } 34 | 35 | // How To Kill Disgusting If Statement? 36 | -------------------------------------------------------------------------------- /14-kill-if/responsibility_chain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Rule interface { 6 | Check(sellInfo *SellInfo) bool 7 | } 8 | 9 | func Chain(sellInfo *SellInfo, rules ...Rule) bool { 10 | for _, r := range rules { 11 | if !r.Check(sellInfo) { 12 | return false 13 | } 14 | } 15 | return true 16 | } 17 | 18 | func main() { 19 | sellInfo := &SellInfo{ 20 | Price: 1.0, 21 | OrderCount: 1, 22 | TotalCount: 20, 23 | MemberShip: 1, 24 | } 25 | 26 | rules := []Rule{ 27 | &PriceRule{}, 28 | &OrderCountRule{}, 29 | &MemberShipRule{}, 30 | &DiscountRule{}, 31 | //... 32 | 33 | } 34 | r := Chain(sellInfo, rules...) 35 | fmt.Println(r) 36 | 37 | } 38 | 39 | type PriceRule struct{} 40 | 41 | func (pr *PriceRule) Check(sellInfo *SellInfo) bool { 42 | return sellInfo.Price > 0 43 | } 44 | 45 | type OrderCountRule struct{} 46 | 47 | func (ocr *OrderCountRule) Check(sellInfo *SellInfo) bool { 48 | return sellInfo.TotalCount > sellInfo.OrderCount 49 | } 50 | 51 | type MemberShipRule struct{} 52 | 53 | func (msr *MemberShipRule) Check(sellInfo *SellInfo) bool { 54 | return sellInfo.MemberShip == 1 55 | } 56 | 57 | type DiscountRule struct{} 58 | 59 | func (dr *DiscountRule) Check(sellInfo *SellInfo) bool { 60 | return sellInfo.Price < 100 && sellInfo.MemberShip == 2 61 | } 62 | -------------------------------------------------------------------------------- /15-table-drive-test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 222@qq.com --> 222, qq.com 4 | func SplitEmail(email string) (username, domain string) { 5 | atIndex := 0 6 | for i := 0; i < len(email); i++ { 7 | if email[i] == '@' { 8 | atIndex = i 9 | break 10 | } 11 | } 12 | username = email[:atIndex] 13 | domain = email[atIndex+1:] 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /15-table-drive-test/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-playground/assert/v2" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | //bad case 11 | func TestSplitEmail(t *testing.T) { 12 | username, domain := SplitEmail("1233@qq.com") 13 | require.NoError(t, nil) 14 | assert.Equal(t, username, "1233") 15 | assert.Equal(t, domain, "qq.com") 16 | 17 | username, domain = SplitEmail("22qqew@gami.com") 18 | require.NoError(t, nil) 19 | assert.Equal(t, username, "22qqew") 20 | assert.Equal(t, domain, "gami.com") 21 | 22 | username, domain = SplitEmail("123213@88.com") 23 | require.NoError(t, nil) 24 | assert.Equal(t, username, "123213") 25 | assert.Equal(t, domain, "88.com") 26 | 27 | username, domain = SplitEmail("123213@228.com") 28 | require.NoError(t, nil) 29 | assert.Equal(t, username, "123213") 30 | assert.Equal(t, domain, "228.com") 31 | 32 | } 33 | 34 | // table drive test 35 | func TestSplitEmail2(t *testing.T) { 36 | tests := []struct { 37 | name string 38 | email string 39 | username string 40 | domain string 41 | }{ 42 | { 43 | name: "test 1", 44 | email: "22qqew@gami.com", 45 | username: "22qqew", 46 | domain: "gami.com", 47 | }, 48 | { 49 | name: "test 2", 50 | email: "1233@qq.com", 51 | username: "1233", 52 | domain: "qq.com", 53 | }, 54 | } 55 | 56 | for _, tt := range tests { 57 | t.Run(tt.name, func(t *testing.T) { 58 | username, domain := SplitEmail(tt.email) 59 | require.NoError(t, nil) 60 | assert.Equal(t, username, tt.username) 61 | assert.Equal(t, domain, tt.domain) 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /16-fuzzy-test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 222@qq.com --> 222, qq.com 4 | func EmailRead(email string) (username, domain string) { 5 | if email == "" { 6 | return 7 | } 8 | atIndex := 0 9 | for i := 0; i < len(email); i++ { 10 | if email[i] == '@' { 11 | atIndex = i 12 | break 13 | } 14 | } 15 | username = email[:atIndex] 16 | domain = email[atIndex+1:] 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /16-fuzzy-test/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/go-playground/assert" 8 | ) 9 | 10 | func TestEmailRead(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | email string 14 | wantU string 15 | wantD string 16 | }{ 17 | { 18 | "test 1", 19 | "22@11.com", 20 | "22", 21 | "11.com", 22 | }, 23 | { 24 | "test 2", 25 | "qwq@qq.com", 26 | "qwq", 27 | "qq.com", 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | u, d := EmailRead(tt.email) 33 | if u != tt.wantU { 34 | t.Errorf("EmailRead() gotUsername = %v, want %v", u, tt.wantU) 35 | } 36 | if d != tt.wantD { 37 | t.Errorf("EmailRead() gotDomain = %v, want %v", d, tt.wantD) 38 | } 39 | }) 40 | } 41 | } 42 | 43 | func FuzzTestEmailRead(f *testing.F) { 44 | testcases := []string{ 45 | "222@11.com", 46 | "12313@qq.com", 47 | "22@222.com", 48 | } 49 | for _, tc := range testcases { 50 | f.Add(tc) 51 | } 52 | f.Fuzz(func(t *testing.T, email string) { 53 | username, domain := 54 | EmailRead(email) 55 | if strings.Contains(email, "@") { 56 | assert.Equal( 57 | t, username+"@"+domain, email) 58 | } 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /17-guard-clauses/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // nested if statements >> bad 4 | func winLottery(bmi float64) bool { 5 | if bmi >= 18.5 { 6 | if bmi <= 24.9 { 7 | return true 8 | } else { 9 | if bmi == 99.0 { 10 | return true 11 | } else { 12 | return false 13 | } 14 | } 15 | } else { 16 | return false 17 | } 18 | } 19 | 20 | // guard clauses >> good 21 | func winLottery_(bmi float64) bool { 22 | if bmi == 99.0 { 23 | return true 24 | } 25 | if bmi < 18.5 { 26 | return false 27 | } 28 | if bmi > 24.9 { 29 | return false 30 | } 31 | return true 32 | } 33 | -------------------------------------------------------------------------------- /18-di-with-wire/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Meta struct{} 6 | type Message struct { 7 | msg string 8 | r Meta 9 | } 10 | type Mouth struct{ Message Message } 11 | type Hand struct{ Message Message } 12 | type People struct { 13 | Mouth Mouth 14 | Hand Hand 15 | //.. 16 | } 17 | 18 | func main_() { 19 | meta := NewMeta() 20 | message := NewMessage("A Hi To U", meta) 21 | aMouth := NewMouth(message) 22 | aHand := NewHand(message) 23 | people := NewPeople(aMouth, aHand) 24 | people.Hi() 25 | } 26 | 27 | //use wire 28 | func main() { 29 | p := InitAPeople("Nice to meet you!") 30 | p.Hi() 31 | } 32 | func NewMeta() Meta { 33 | return Meta{} 34 | } 35 | func NewMessage(msg string, r Meta) Message { 36 | return Message{ 37 | msg: msg, 38 | r: r, 39 | } 40 | } 41 | func NewMouth(m Message) Mouth { 42 | return Mouth{Message: m} 43 | } 44 | func NewHand(m Message) Hand { 45 | return Hand{Message: m} 46 | } 47 | func NewPeople(g Mouth, t Hand) People { 48 | return People{Mouth: g, Hand: t} 49 | } 50 | 51 | func (p People) Hi() { 52 | msg := p.Mouth.Greet() 53 | fmt.Println(msg) 54 | } 55 | func (g Mouth) Greet() string { 56 | return g.Message.msg 57 | } 58 | -------------------------------------------------------------------------------- /18-di-with-wire/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | // +build wireinject 3 | 4 | package main 5 | 6 | import "github.com/google/wire" 7 | 8 | func InitAPeople(msg string) People { 9 | wire.Build( 10 | NewPeople, 11 | NewMouth, 12 | NewHand, 13 | //.. 14 | NewMessage, 15 | NewMeta) 16 | return People{} 17 | } 18 | -------------------------------------------------------------------------------- /18-di-with-wire/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package main 8 | 9 | // Injectors from wire.go: 10 | 11 | func InitAPeople(msg string) People { 12 | meta := NewMeta() 13 | message := NewMessage(msg, meta) 14 | mouth := NewMouth(message) 15 | hand := NewHand(message) 16 | people := NewPeople(mouth, hand) 17 | return people 18 | } 19 | -------------------------------------------------------------------------------- /19-json-struct-tip/tip0.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Tip0: 9 | // Outer Struct Override Inner Struct 10 | 11 | type Inner struct { 12 | Name string `json:"name" ` 13 | Price int `json:"price"` 14 | } 15 | 16 | type Outer struct { 17 | Inner 18 | Price string `json:"price"` 19 | } 20 | 21 | func main0() { 22 | m := new(Outer) 23 | 24 | m.Name = "river" 25 | m.Price = "100" 26 | 27 | r, _ := json.Marshal(m) 28 | fmt.Println(string(r)) 29 | } 30 | -------------------------------------------------------------------------------- /19-json-struct-tip/tip1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Tip1: ignore field 2 ways: 9 | 10 | type Meta struct { 11 | Username string `json:"username" :"Username"` 12 | Password string `json:"password" :"Password"` 13 | // many more fields… 14 | } 15 | 16 | type MetaProxy struct { 17 | Meta 18 | // ignore by downCase 19 | score string `json:"score" :"Score"` 20 | // ignore by tag 21 | Password string `json:"-"` 22 | } 23 | 24 | func main1() { 25 | m := new(MetaProxy) 26 | 27 | m.Username = "river" 28 | m.score = "100" 29 | m.Password = "123456" 30 | 31 | r, _ := json.Marshal(m) 32 | fmt.Println(string(r)) 33 | // {"username":"river",Password:""} 34 | } 35 | -------------------------------------------------------------------------------- /19-json-struct-tip/tip2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Tip2: Omitting empty values 9 | 10 | type Resp struct { 11 | Code int `json:"code"` 12 | Msg string `json:"msg"` 13 | ErrorInf string `json:"error"` 14 | } 15 | 16 | type RespProxy struct { 17 | Resp 18 | ErrorInf string `json:"error,omitempty"` 19 | } 20 | 21 | func main2() { 22 | r := new(RespProxy) 23 | r.Code = 200 24 | r.Msg = "ok" 25 | r.ErrorInf = "" 26 | 27 | r2, _ := json.Marshal(r) 28 | fmt.Println(string(r2)) 29 | // {"code":200,"msg":"ok"} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /19-json-struct-tip/tip3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Tip3: type convert 9 | 10 | type Base struct { 11 | Code int `json:"code,string"` 12 | Msg string `json:"msg"` 13 | } 14 | 15 | func main3() { 16 | r := new(Base) 17 | r.Code = 200 18 | r.Msg = "ok" 19 | 20 | r2, _ := json.Marshal(r) 21 | fmt.Println(string(r2)) 22 | // {"code":"200","msg":"ok"} 23 | 24 | } 25 | -------------------------------------------------------------------------------- /19-json-struct-tip/tip4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Tip4: Store Raw Data Without Type 9 | 10 | type Raw struct { 11 | Code int `json:"code"` 12 | Resp json.RawMessage `json:"resp"` 13 | } 14 | 15 | func main() { 16 | rawStr := `{ 17 | "code":200, 18 | "resp":{"name":"river", "age":18}}` 19 | var raw Raw 20 | json.Unmarshal([]byte(rawStr), &raw) 21 | fmt.Println(string(raw.Resp)) 22 | //{"name":"river", "age":18} 23 | } 24 | -------------------------------------------------------------------------------- /20-json-track/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type Score struct { 10 | Name string 11 | time.Time 12 | } 13 | 14 | func main() { 15 | s := Score{ 16 | Name: "river", 17 | Time: time.Now(), 18 | } 19 | r, err := json.Marshal(s) 20 | if err != nil { 21 | return 22 | } 23 | fmt.Printf("json: %s\n", string(r)) 24 | 25 | /* 26 | guess the output 27 | 28 | Is { name: "river", 29 | time: "2023-02-21T23:04:34.042027+08:00"} 30 | ?? 31 | */ 32 | 33 | } 34 | -------------------------------------------------------------------------------- /21-functional-dao/go.mod: -------------------------------------------------------------------------------- 1 | module functional-dao 2 | 3 | go 1.18 4 | 5 | require gorm.io/driver/sqlite v1.4.4 6 | 7 | require ( 8 | github.com/jinzhu/inflection v1.0.0 // indirect 9 | github.com/jinzhu/now v1.1.5 // indirect 10 | github.com/mattn/go-sqlite3 v1.14.15 // indirect 11 | gorm.io/gorm v1.24.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /21-functional-dao/go.sum: -------------------------------------------------------------------------------- 1 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 2 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 3 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 4 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 5 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 6 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 7 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 8 | gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= 9 | gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= 10 | gorm.io/gorm v1.24.0 h1:j/CoiSm6xpRpmzbFJsQHYj+I8bGYWLXVHeYEyyKlF74= 11 | gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= 12 | -------------------------------------------------------------------------------- /21-functional-dao/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "functional-dao/model" 4 | 5 | func main() { 6 | model.InitDB() 7 | model.AddFakeBook() 8 | model.ClientExample2() 9 | } 10 | -------------------------------------------------------------------------------- /21-functional-dao/model/common.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/driver/sqlite" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | var db *gorm.DB 9 | 10 | type Book struct { 11 | gorm.Model 12 | ISBN int64 `json:"isbn"` 13 | Name string `json:"name"` 14 | Price int `json:"price"` 15 | Category int `json:"category"` 16 | } 17 | 18 | func ConnectDB() *gorm.DB { 19 | d, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{}) 20 | if err != nil { 21 | panic("Failed to connect to database!") 22 | } 23 | db = d 24 | return nil 25 | } 26 | 27 | func GetDB() *gorm.DB { 28 | return db 29 | } 30 | 31 | func InitDB() { 32 | ConnectDB() 33 | db.AutoMigrate(&Book{}) 34 | } 35 | 36 | func AddFakeBook() { 37 | db.Create(&Book{ 38 | ISBN: 22, 39 | Name: "Book 22", 40 | Price: 100, 41 | Category: 2, 42 | }) 43 | 44 | db.Create(&Book{ 45 | ISBN: 23, 46 | Name: "Book 23", 47 | Price: 200, 48 | Category: 2, 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /21-functional-dao/model/mode_config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "fmt" 4 | 5 | type BookQuery struct { 6 | ID int 7 | ISBN int64 8 | Name string 9 | Price int 10 | } 11 | 12 | // Better 1: Struct + switch 13 | func FindBookByInfo2(info BookQuery) ([]Book, error) { 14 | var books []Book 15 | db := GetDB() 16 | switch { 17 | case info.ID != 0: 18 | db = db.Where("id = ?", info.ID) 19 | case info.ISBN != 0: 20 | db = db.Where("isbn = ?", info.ISBN) 21 | case info.Name != "": 22 | db = db.Where("name = ?", info.Name) 23 | case info.Price != 0: 24 | db = db.Where("price = ?", info.Price) 25 | } 26 | result := db.Find(&books) 27 | return books, result.Error 28 | } 29 | 30 | //client code 31 | func ClientExample1() { 32 | 33 | books, _ := FindBookByInfo2(BookQuery{ 34 | ISBN: 22, 35 | }) 36 | fmt.Printf("%+v", books) 37 | } 38 | -------------------------------------------------------------------------------- /21-functional-dao/model/model_functional.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type Option func(*gorm.DB) *gorm.DB 10 | 11 | func WithCategory(category int) Option { 12 | return func(db *gorm.DB) *gorm.DB { 13 | return db.Where("category = ?", category) 14 | } 15 | } 16 | func WithPrice(price int) Option { 17 | return func(db *gorm.DB) *gorm.DB { 18 | return db.Where("price = ?", price) 19 | } 20 | } 21 | func WithPriceRange(min, max int) Option { 22 | return func(db *gorm.DB) *gorm.DB { 23 | return db.Where("price >= ? AND price <= ?", min, max) 24 | } 25 | } 26 | func TableName(tableName string) Option { 27 | return func(db *gorm.DB) *gorm.DB { 28 | return db.Table(tableName) 29 | } 30 | } 31 | 32 | func GetBookByOptions(options ...Option) ([]Book, error) { 33 | var books []Book 34 | db := GetDB() 35 | for _, option := range options { 36 | db = option(db) 37 | } 38 | result := db.Find(&books) 39 | return books, result.Error 40 | } 41 | 42 | // client code 43 | func ClientExample2() { 44 | books, _ := GetBookByOptions( 45 | WithPrice(200), 46 | WithCategory(2), 47 | TableName("books"), 48 | //.. 49 | ) 50 | fmt.Println(books) 51 | } 52 | -------------------------------------------------------------------------------- /21-functional-dao/model/model_raw.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "fmt" 4 | 5 | func GetBookByID(id int) (Book, error) { 6 | var book Book 7 | db := GetDB() 8 | result := db.First(&book, id) 9 | return book, result.Error 10 | } 11 | func GetBookByISBN(isbn int64) (Book, error) { 12 | var book Book 13 | db := GetDB() 14 | result := db.Where("isbn = ?", isbn).First(&book) 15 | return book, result.Error 16 | } 17 | func GetBookByCategory(category int) ([]Book, error) { 18 | var books []Book 19 | db := GetDB() 20 | result := db.Where("category = ?", category).Find(&books) 21 | return books, result.Error 22 | } 23 | func GetBookByPrice(price int) ([]Book, error) { 24 | var books []Book 25 | db := GetDB() 26 | result := db.Where("price = ?", price).Find(&books) 27 | return books, result.Error 28 | } 29 | 30 | //client code 31 | func ClientExample() { 32 | r, _ := GetBookByPrice(200) 33 | fmt.Println(r) 34 | } 35 | -------------------------------------------------------------------------------- /22-timewheel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "goog/22-timewheel/txt" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/rfyiamcool/go-timewheel" 10 | ) 11 | 12 | func rawTest(count int, loop int) { 13 | var m1, m2 runtime.MemStats 14 | runtime.ReadMemStats(&m1) 15 | var totalTime time.Duration 16 | var totalMemory uint64 17 | 18 | for index := 0; index < loop; index++ { 19 | start := time.Now() 20 | for index := 0; index < count; index++ { 21 | txt.NewSMS("Hello, world!") 22 | } 23 | 24 | loopTime := time.Since(start) 25 | runtime.ReadMemStats(&m2) 26 | totalMemory += m2.TotalAlloc - m1.TotalAlloc 27 | m1 = m2 28 | totalTime += loopTime 29 | } 30 | fmt.Println("-----timer result-----") 31 | fmt.Printf("loop: %d , time : %v, memory : %dkb\n", 3, totalTime/3, totalMemory/3) 32 | } 33 | 34 | func twTest(count int, loop int) { 35 | tw, err := 36 | timewheel.NewTimeWheel(1*time.Second, 20) 37 | if err != nil { 38 | panic(err) 39 | } 40 | tw.Start() 41 | defer tw.Stop() 42 | var m1, m2 runtime.MemStats 43 | runtime.ReadMemStats(&m1) 44 | var totalTime time.Duration 45 | var totalMemory uint64 46 | 47 | for index := 0; index < loop; index++ { 48 | start := time.Now() 49 | for index := 0; index < count; index++ { 50 | txt.NewSMS_("Hello, world!", tw) 51 | } 52 | loopTime := time.Since(start) 53 | runtime.ReadMemStats(&m2) 54 | totalMemory += m2.TotalAlloc - m1.TotalAlloc 55 | m1 = m2 56 | totalTime += loopTime 57 | } 58 | fmt.Println("-----timewheel result-----") 59 | fmt.Printf("loop: %d , time : %v, memory : %dkb\n", 3, totalTime/3, totalMemory/3) 60 | } 61 | 62 | func main() { 63 | rawTest(500000, 3) 64 | twTest(500000, 3) 65 | } 66 | -------------------------------------------------------------------------------- /22-timewheel/txt/raw.go: -------------------------------------------------------------------------------- 1 | package txt 2 | 3 | import "time" 4 | 5 | type sms_raw struct { 6 | text string 7 | status string 8 | timer *time.Timer 9 | } 10 | 11 | func NewSMS(text string) *sms_raw { 12 | s := &sms_raw{ 13 | text: text, 14 | status: "unverified", 15 | } 16 | s.timer = time.NewTimer(3 * time.Second) 17 | go func() { 18 | <-s.timer.C 19 | if s.status == "unverified" { 20 | s = nil 21 | } 22 | }() 23 | return s 24 | } 25 | 26 | func (s *sms_raw) Verify() { 27 | s.status = "verified" 28 | if !s.timer.Stop() && s.timer != nil { 29 | <-s.timer.C 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /22-timewheel/txt/tw.go: -------------------------------------------------------------------------------- 1 | package txt 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rfyiamcool/go-timewheel" 7 | ) 8 | 9 | type sms_tw struct { 10 | text string 11 | status string 12 | timer *timewheel.Task 13 | } 14 | 15 | /* 16 | tw, err := timewheel.NewTimeWheel(1*time.Second, 20) 17 | tw.Start() 18 | defer tw.Stop() 19 | */ 20 | func NewSMS_(text string, tw *timewheel.TimeWheel) *sms_tw { 21 | s := &sms_tw{ 22 | text: text, 23 | status: "unverified", 24 | } 25 | task := tw.Add(3*time.Second, func() { 26 | if s.status == "unverified" { 27 | s = nil 28 | } 29 | }) 30 | s.timer = task 31 | return s 32 | } 33 | 34 | func (s *sms_tw) Verify_(tw *timewheel.TimeWheel) { 35 | s.status = "verified" 36 | tw.Remove(s.timer) 37 | } 38 | -------------------------------------------------------------------------------- /23-kill-err/killer/nil-style.go: -------------------------------------------------------------------------------- 1 | package killer 2 | 3 | import "fmt" 4 | 5 | type Book struct { 6 | Name string 7 | Price int 8 | Store int 9 | Member int 10 | } 11 | 12 | func clientExample() { 13 | book := &Book{ 14 | Name: "golang", 15 | Price: 100, 16 | Store: 100, 17 | Member: 1, 18 | } 19 | err := book.CalcDiscount(99) 20 | if err != nil { 21 | return 22 | } 23 | err = book.JuedeIfStore() 24 | if err != nil { 25 | return 26 | } 27 | err = book.IfSale() 28 | if err != nil { 29 | return 30 | } 31 | err = book.CalcTotal() 32 | if err != nil { 33 | return 34 | } 35 | fmt.Println("ok") 36 | } 37 | func (b *Book) CalcDiscount(count int) error { 38 | // ... 39 | b.Price = b.Price * count / 100 40 | err := SomeError() 41 | if err != nil { 42 | return err 43 | } 44 | return nil 45 | } 46 | func (b *Book) JuedeIfStore() error { 47 | 48 | return nil 49 | } 50 | func (b *Book) IfSale() error { 51 | // ... 52 | return nil 53 | } 54 | func (b *Book) CalcTotal() error { 55 | // ... 56 | return nil 57 | } 58 | func (b *Book) AddPay() error { 59 | // ... 60 | return nil 61 | } 62 | func (b *Book) AddOrder() error { 63 | // ... 64 | return nil 65 | } 66 | 67 | func SomeError() error { 68 | return fmt.Errorf("some error") 69 | } 70 | -------------------------------------------------------------------------------- /23-kill-err/killer/struct-style.go: -------------------------------------------------------------------------------- 1 | package killer 2 | 3 | import "fmt" 4 | 5 | func someError() error { 6 | return fmt.Errorf("some error") 7 | } 8 | 9 | type Book2 struct { 10 | Name string 11 | Price int 12 | Store int 13 | Member int 14 | error error 15 | } 16 | 17 | func clientExample2() { 18 | book2 := &Book2{ 19 | Name: "golang", 20 | Price: 100, 21 | Store: 100, 22 | Member: 1, 23 | } 24 | book2.CalcDiscount(99). 25 | JuedeIfStore(). 26 | IfSale(). 27 | AddPay(). 28 | AddOrder(). 29 | CheckAll() 30 | 31 | fmt.Println("ok") 32 | 33 | } 34 | 35 | func (b *Book2) CalcDiscount(count int) *Book2 { 36 | if b.error != nil { 37 | return b 38 | } 39 | // 具体的业务逻辑.. 40 | b.error = someError() 41 | return b 42 | } 43 | 44 | func (b *Book2) JuedeIfStore() *Book2 { 45 | if b.error != nil { 46 | return b 47 | } 48 | // 具体的业务逻辑 49 | return b 50 | } 51 | func (b *Book2) IfSale() *Book2 { 52 | if b.error != nil { 53 | return b 54 | } 55 | return b 56 | } 57 | func (b *Book2) CalcTotal() *Book2 { 58 | if b.error != nil { 59 | return b 60 | } 61 | return b 62 | } 63 | func (b *Book2) AddPay() *Book2 { 64 | if b.error != nil { 65 | return b 66 | } 67 | return b 68 | } 69 | func (b *Book2) AddOrder() *Book2 { 70 | if b.error != nil { 71 | return b 72 | } 73 | return b 74 | } 75 | 76 | func (b *Book2) CheckAll() *Book2 { 77 | if b.error != nil { 78 | return b 79 | } 80 | return b 81 | } 82 | -------------------------------------------------------------------------------- /23-kill-err/main.go: -------------------------------------------------------------------------------- 1 | package killerr 2 | -------------------------------------------------------------------------------- /24-error-stack/errorSimple/better-error.go: -------------------------------------------------------------------------------- 1 | package errorSimple 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func aFunc_() error { 10 | err := errors.New("A Mini Error !") 11 | if err != nil { 12 | return errors.Wrap(err, "aFunc_") 13 | } 14 | return nil 15 | } 16 | 17 | func anotherFunc_() error { 18 | err := aFunc_() 19 | if err != nil { 20 | return errors.Wrap(err, "anotherFunc_") 21 | } 22 | return nil 23 | } 24 | 25 | func StartClient2() { 26 | err := anotherFunc_() 27 | if err != nil { 28 | fmt.Printf("stack trace: %+v\n", errors.Cause(err)) 29 | return 30 | } 31 | fmt.Println("ok") 32 | } 33 | -------------------------------------------------------------------------------- /24-error-stack/errorSimple/raw.go: -------------------------------------------------------------------------------- 1 | package errorSimple 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | func aFunc() error { 9 | err := errors.New("inFunc error") 10 | if err != nil { 11 | return errors.Wrap(err, "aFunc") 12 | } 13 | return nil 14 | } 15 | 16 | func anotherFunc() error { 17 | err := aFunc() 18 | if err != nil { 19 | return errors.Wrap(err, "anotherFunc") 20 | } 21 | return nil 22 | } 23 | 24 | func StartExample() { 25 | err := anotherFunc() 26 | if err != nil { 27 | fmt.Printf("main error: %+v\n", 28 | errors.Cause(err)) 29 | return 30 | } 31 | fmt.Println("ok") 32 | } 33 | 34 | -------------------------------------------------------------------------------- /24-error-stack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "goog/24-error-stack/errorSimple" 5 | ) 6 | 7 | func main() { 8 | errorSimple.StartExample() 9 | // errorSimple.StartExample2() 10 | } 11 | -------------------------------------------------------------------------------- /25-refactor-extract/extract.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func (o Order) BasePrice() float64 { 9 | return o.Quantity * o.ItemPrice 10 | } 11 | 12 | func QuantityDiscount(o Order) float64 { 13 | discount := 0.0 14 | if o.Quantity > 500 { 15 | discount = 0.05 16 | } 17 | return o.BasePrice() * discount 18 | } 19 | 20 | func Shipping(o Order) float64 { 21 | shipping := o.BasePrice() * 0.1 22 | if shipping > 100 { 23 | shipping = 100 24 | } 25 | return shipping 26 | } 27 | 28 | func Price(o Order) float64 { 29 | return o.BasePrice() - 30 | QuantityDiscount(o) + 31 | Shipping(o) 32 | } 33 | 34 | func PriceQuick(o Order) float64 { 35 | var wg sync.WaitGroup 36 | var quantityDiscount float64 37 | var shipping float64 38 | 39 | basePrice := o.BasePrice() 40 | go func() { 41 | defer wg.Done() 42 | quantityDiscount = QuantityDiscount(o) 43 | }() 44 | go func() { 45 | defer wg.Done() 46 | shipping = Shipping(o) 47 | }() 48 | wg.Wait() 49 | 50 | return basePrice - quantityDiscount + shipping 51 | } 52 | 53 | func main_() { 54 | order := Order{Quantity: 600, ItemPrice: 10.0} 55 | fmt.Println(Price(order)) 56 | } 57 | -------------------------------------------------------------------------------- /25-refactor-extract/raw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "math" 4 | 5 | type Order struct { 6 | Quantity float64 7 | ItemPrice float64 8 | } 9 | 10 | func price(order Order) float64 { 11 | // price formula: 12 | // base price - quantity discount + shipping 13 | return order.Quantity*order.ItemPrice - 14 | math.Max(0, order.Quantity-500)*order.ItemPrice*0.05 + 15 | math.Min(order.Quantity*order.ItemPrice*0.1, 100) 16 | } 17 | 18 | func main() { 19 | o := Order{ 20 | Quantity: 1000, 21 | ItemPrice: 10, 22 | } 23 | println(price(o)) 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module goog 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/wire v0.5.0 7 | github.com/stretchr/testify v1.8.1 8 | ) 9 | 10 | require ( 11 | github.com/go-playground/assert v1.2.1 12 | github.com/rfyiamcool/go-timewheel v1.1.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/go-playground/assert/v2 v2.2.0 18 | github.com/pkg/errors v0.9.1 19 | github.com/pmezard/go-difflib v1.0.0 // indirect 20 | gopkg.in/yaml.v3 v3.0.1 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/go-playground/assert v1.2.1 h1:ad06XqC+TOv0nJWnbULSlh3ehp5uLuQEojZY5Tq8RgI= 5 | github.com/go-playground/assert v1.2.1/go.mod h1:Lgy+k19nOB/wQG/fVSQ7rra5qYugmytMQqvQ2dgjWn8= 6 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 7 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 8 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 9 | github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 10 | github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= 11 | github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= 12 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 13 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 14 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 15 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 16 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 17 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 18 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/rfyiamcool/go-timewheel v1.1.0 h1:iEQb2pdDkiJEEFQf/ybDN3FKeje42qU+e3kXa2wbyHo= 22 | github.com/rfyiamcool/go-timewheel v1.1.0/go.mod h1:aKdsHKoLC2/Ax6WDNk2pOsNto6wlGahKBwBU3U0Jgnw= 23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 24 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 25 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 26 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 29 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 30 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 32 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 33 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 34 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 35 | golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 36 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 37 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 38 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 39 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 40 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 41 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 42 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Golang Trick 2 | 3 | ## ❤️ Help other fellow developers 4 | 5 | Sharing is free but caring is priceless. [So, now please click here](https://www.bilibili.com/video/BV19e4y1A7DD/) and share this repository on Bilibili. 6 | 7 | ## Let's Gogogogogo! 8 | 9 | * **[如何让idea快速实现接口](https://www.bilibili.com/video/BV19e4y1A7DD)** 10 | 11 | _虽然go没有`imple`关键字,但依旧不影响gopher发挥._ 12 | * **[可变参数`...`操作神奇切片](https://www.bilibili.com/video/BV1CA41167dM)** 13 | 14 | _神奇...的用法._ 15 | * **[interface的nil陷阱](https://www.bilibili.com/video/BV1uT41197i4)** 16 | 17 | _golang的`interface{}`可没有ts的`any`省心._ 18 | * **[给函数返回值起好名字](https://www.bilibili.com/video/BV1dT411D7vy)** 19 | 20 | _好的习惯会给无聊代码带来一阵清风._ 21 | 22 | * **[你关注过range后面的表达式吗](https://www.bilibili.com/video/BV15Y411i7oz/)** 23 | 24 | _不同于for,range后接的表达式只会被求值一次._ 25 | 26 | * **[永远不要在循环中更新map](https://www.bilibili.com/video/BV15Y411i7oz)** 27 | 28 | _map是动态数据类型,你根本不知道自己的数据存储在个桶._ 29 | 30 | * **[如何更新map中的struct](https://www.bilibili.com/video/BV1ER4y1B7WY/)** 31 | 32 | _map如果存储struct作为value,不能修改它的字段._ 33 | 34 | * **[如何优雅地实现继承](https://www.bilibili.com/video/BV1Ge4y1w7eL)** 35 | 36 | _结构体嵌套有用,但是并非处处有用._ 37 | 38 | * **[方法接收器悖论](https://www.bilibili.com/video/BV1Ys4y1a7xy/)** 39 | 40 | _深刻摸索两种方法集和接口的关系._ 41 | 42 | * **[闭包为何具有记忆效应](https://www.bilibili.com/video/BV14D4y1w7xP/)** 43 | 44 | _利用内存逃逸分析解释闭包的变量记忆._ 45 | 46 | * **[责任链带你逃离IF地狱](https://www.bilibili.com/video/BV13A411U7Z4/)** 47 | 48 | _如果业务层条件判断过多,请选择责任链吧._ 49 | 50 | * **[探索map循环随机的根本原因](https://www.bilibili.com/video/BV1JT411D7gB/)** 51 | 52 | _从golan源代码简单看看为何map是没有顺序的._ 53 | 54 | * **[优雅的多用例单元测试](https://www.bilibili.com/video/BV1Wj411A7CN/)** 55 | 56 | _如果针对同一函数测试用例过多,试试表驱动测试._ 57 | 58 | * **[利用Fuzz Test消灭BUG](https://www.bilibili.com/video/BV1KT411U7tF/)** 59 | 60 | _单元测试不放心,试试模糊测试._ 61 | 62 | * **[利用卫语句消除IF嵌套](https://www.bilibili.com/video/BV18e4y1c7GW/)** 63 | 64 | _如果某些分支为异常层,就利用Guard Clause让他们提前返回吧._ 65 | 66 | * **[如何漂亮地实现依赖注入](https://www.bilibili.com/video/BV1Db411X7dU/)** 67 | 68 | _复杂项目不要手动注入依赖,选择WIRE解放生产力._ 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | --- 79 | 80 | ## License 81 | 82 | Whole materials are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
83 | 84 | Creative Commons License 85 | --------------------------------------------------------------------------------