go mod modfile 引入
练习
目录结构:
common
|-go.mod
mod1
|-demo14mod
|-go.mod
|-main.go
代码
common/go.mod
module demo14mod go 1.13 require github.com/pkg/errors v0.9.1
mod1/main.go
package main import ( "fmt" ) func main() { fmt.Println("Hello world!") }
进入mod1目录,go 1.13 下执行
go run main.go
报错信息如下:
go: cannot determine module path for source directory D:\14\mod1 (outside GOPATH, module path must be specified) Example usage: 'go mod init example.com/m' to initialize a v0 or v1 module 'go mod init example.com/m/v2' to initialize a v2 module Run 'go help mod init' for more information.
go 1.14下执行
go run -modfile=../common/go.mod main.go
能够正常输出
Hello world!
聚合接口
interface.go
package main import "fmt" type Foo interface { Incr(int) int FooName() string } type Bar interface { Incr(int) int BarName() string } type FooBar interface { Foo Bar } type A struct { FooBar } func (a A) Incr(x int) int { return x + 1 } func (a A) FooName() string { return "foo_a" } func (a A) BarName() string { return "bar_a" } func main() { var a = A{} fmt.Println(a.Incr(1)) }
在go 1.13下运行
go run interface.go
报错信息如下:
# command-line-arguments .\interface.go:17:2: duplicate method Incr
在go 1.14下运行
go run interface.go
能够正常输出:
2
defer 0消耗
参考:http://xiaorui.cc/archives/6579
goroutine异步抢占
参考:http://xiaorui.cc/archives/6535
计时器优化
参考:http://xiaorui.cc/archives/6483
高性能内存分配
参考:http://xiaorui.cc/archives/6613
go testing包
foo.go
package test func foo(i int) int { return i + 1 }
foo_test.go
package test import ( "testing" "time" ) func TestFoo(t *testing.T) { a := 2 b:= foo(a) t.Log("test log info") time.Sleep(2 * time.Second) if b != 3 { t.Error("error foo") } }
go 1.13下执行
go test ./ -v
输出结果:
=== RUN TestFoo --- PASS: TestFoo (2.00s) foo_test.go:11: test log info PASS ok api/tests/14/test 2.639s
go 1.14下执行
go test ./ -v
输出结果
=== RUN TestFoo TestFoo: foo_test.go:11: test log info --- PASS: TestFoo (2.00s) PASS ok _/mnt/d/docker/nginx/www/kuaibu/go/src/api/tests/14/test 2.014s
总结:go 1.13下是先sleep 再输出;go1.14下是先输出再sleep。增加了可调试性。
Cleanup函数
foo_test.go 修改后
package test import ( "fmt" "testing" "time" ) func TestFoo(t *testing.T) { t.Cleanup(func() { // 清空数据库操作等。。。。 fmt.Println("clean up do something") }) a := 2 b:= foo(a) t.Log("test log info") time.Sleep(2 * time.Second) if b != 3 { t.Error("error foo") } }
只能在go 1.14下执行
go test ./ -v
输出结果:
=== RUN TestFoo TestFoo: foo_test.go:17: test log info clean up do something --- PASS: TestFoo (2.00s) PASS ok _/mnt/d/docker/nginx/www/kuaibu/go/src/api/tests/14/test 2.009s
可用于单元测试完成后的数据恢复操作。
maphash
hash.go
package main import ( "fmt" "hash/maphash" ) func main() { var h maphash.Hash h.WriteString("hello, ") fmt.Printf("%#x\n", h.Sum64()) h.Write([]byte{'w', 'o', 'r', 'l', 'd'}) fmt.Printf("%#x\n", h.Sum64()) h.Reset() var h2 maphash.Hash h2.SetSeed(h.Seed()) h.WriteString("same") h2.WriteString("same") fmt.Printf("%#x == %#x\n", h.Sum64(), h2.Sum64()) }
在go 1.14下执行
go run hash.go
运行结果:
0x88f5739aaae578de 0x3fd68a56fae928bd 0x496876308f9ef321 == 0x496876308f9ef321
InputOffset() json解析的位置
json.go
package main import ( "encoding/json" "fmt" "strings" "io" "log" ) func main() { const jsonStream = ` {"Name": "Ed", "Text": "Knock knock."} {"Name": "Sam", "Text": "Who's there?"} {"Name": "Ed", "Text": "Go fmt."} {"Name": "Sam", "Text": "Go fmt who?"} {"Name": "Ed", "Text": "Go fmt yourself!"} ` type Message struct { Name, Text string } dec := json.NewDecoder(strings.NewReader(jsonStream)) for { var m Message if err := dec.Decode(&m); err == io.EOF { break } else if err != nil { log.Fatal(err) } fmt.Printf("%s: %s\n", m.Name, m.Text) fmt.Println("offset :", dec.InputOffset()) } }
在 go 1.14下执行
go run json.go
运行结果:
Ed: Knock knock. offset : 40 Sam: Who's there? offset : 81 Ed: Go fmt. offset : 116 Sam: Go fmt who? offset : 156 Ed: Go fmt yourself! offset : 200
ioutil
可以定义缓存名称的随机字符串位置
ioutil.go
package main import ( "io/ioutil" "log" "path/filepath" ) func main() { content := []byte("temporary file's content") dir, err := ioutil.TempDir("/tmp/", "example-ran-*-test") if err != nil { log.Fatal("err") } // defer os.RemoveAll(dir) // clean up tmpfn := filepath.Join(dir, "tmpfile") if err := ioutil.WriteFile(tmpfn, content, 0666); err != nil { log.Fatal(err) } }
在go 1.14下执行
go run ioutil.go
然后再执行
ls /tmp
可以看到如下内容:
example-ran-951862863-test
log.Lmsgprefix
log.go
package main import ( "bytes" "fmt" "log" ) func main() { var ( buf bytes.Buffer logger = log.New(&buf, "logger:", log.Lshortfile)//|log.Lmsgprefix) ) logger.SetPrefix("test prefix: ") logger.Print("Hello, log file!") fmt.Print(&buf) }
在 go 1.13 下执行
go run log.go
显示结果如下:
test prefix: log.go:16: Hello, log file!
编辑log.go
package main import ( "bytes" "fmt" "log" ) func main() { var ( buf bytes.Buffer logger = log.New(&buf, "logger:", log.Lshortfile|log.Lmsgprefix) ) logger.SetPrefix("test prefix: ") logger.Print("Hello, log file!") fmt.Print(&buf) }
在 go 1.14下执行
go run log.go
显示结果如下:
log.go:16: test prefix: Hello, log file!
reflect 包 StructOf 私有字段
struct.go
package main import ( "bytes" "encoding/json" "fmt" "reflect" ) func main() { typ := reflect.StructOf([]reflect.StructField{ { Name: "id", Type: reflect.TypeOf(int(0)), Tag: `json:"id"`, PkgPath: "other/pkg", }, { Name: "Age", Type: reflect.TypeOf(int(0)), Tag:`json:"age"`, }, }) v := reflect.New(typ).Elem() v.Field(1).SetInt(2) s:= v.Addr().Interface() w := new(bytes.Buffer) if err := json.NewEncoder(w).Encode(s); err != nil { panic(err) } fmt.Printf("value: %+v\n", s) fmt.Printf("json: %s", w.Bytes()) }
在 go 1.13下执行
go run struct.go
显示结果如下:
panic: reflect.StructOf: StructOf does not allow unexported fields goroutine 1 [running]: C:/Go/src/reflect/type.go:2362 +0x235d main.main() D:/tests/14/struct.go:11 +0x103 exit status 2
在go 1.14下执行
go run struct.go
显示结果如下:
value: &{id:0 Age:2} json: {"age":2}
runtime包 Goexit
goexit.go
package main import ( "fmt" "runtime" ) func maybeGoexit() { defer func() { fmt.Println(recover()) }() defer panic("cancelled Goexit") runtime.Goexit() } func main() { maybeGoexit() fmt.Println("Hello, playground") }
在go 1.13下执行
go run goexit.go
显示结果如下:
cancelled Goexit Hello, playground
在go 1.14下执行
go run goexit.go
显示结果如下:
cancelled Goexit fatal error: no goroutines (main called runtime.Goexit) - deadlock! runtime stack: runtime.throw(0x4c6d39, 0x36) /mnt/d/go/src/runtime/panic.go:1112 +0x72 runtime.checkdead() /mnt/d/go/src/runtime/proc.go:4407 +0x2f4 runtime.mput(...) /mnt/d/go/src/runtime/proc.go:4818 runtime.stopm() /mnt/d/go/src/runtime/proc.go:1826 +0x95 runtime.findrunnable(0xc000022000, 0x0) /mnt/d/go/src/runtime/proc.go:2360 +0xa0d runtime.schedule() /mnt/d/go/src/runtime/proc.go:2520 +0x2fc runtime.goexit0(0xc000000180) /mnt/d/go/src/runtime/proc.go:2849 +0x1d6 runtime.mcall(0x0) /mnt/d/go/src/runtime/asm_amd64.s:318 +0x5b exit status 2
strcov 包 NumError
numbererr.go
package main import ( "errors" "fmt" "strconv" ) func main() { str := "Not a number" if _, err := strconv.ParseFloat(str, 64); err != nil { if errors.Is(err, strconv.ErrSyntax) { fmt.Println("格式错误") } if errors.Is(err, strconv.ErrRange) { fmt.Println("范围错误") } } }
在go 1.13下执行,没有任何输出。
在go 1.14下执行
go run numbererr.go
显示结果如下:
格式错误
text/template
template.go
package main import ( "html/template" "log" "os" ) func main() { const letter = ` Dear {{.Name}}, {{if (eq .Attended "foo") or (eq .Attended "bar")}} It is a shame you couldn't make it to the wedding. {{- end}} ` type Recipient struct { Name, Gift string Attended string } var recipients = []Recipient{ {"Aunt Mildred", "bone china tea set", "foo"}, } t := template.Must(template.New("letter").Parse(letter)) for _, r := range recipients { err := t.Execute(os.Stdout, r) if err != nil { log.Println("executing template: ", err) } } }
在go 1.13下执行
go run template.go
显示结果如下:
Dear Aunt Mildred, It is a shame you couldn't make it to the wedding.
在 go 1.14下执行
go run template.go
显示结果如下:
Dear Aunt Mildred, 2020/03/12 21:21:02 executing template: template: letter:3:5: executing "letter" at <(eq .Attended "foo") or (eq .Attended "bar")>: can't give argument to non-function eq .Attended "foo"
本文整理来自视频:https://www.bilibili.com/video/av92773498/