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/