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/