Golang 1.14 新特性整理

Jackey Golang 13,536 次浏览 没有评论

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/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Go