Skip to content

Go Tips

1. GOPROXY 配置

因为众所周知的原因,通过 go get 下载代码模块会非常慢,甚至连接失败。此时需要配置代理。

可以参考 Goproxy 中国,配置:

shell
go env -w GOPROXY=https://goproxy.cn,direct

该操作会向 Go 的配置文件中写入相关配置,可以通过 go env GOENV 查看配置文件路径。

2. GOPRIVATE 配置

有些模块是私有的,例如企业内部的私有代码仓库。此时 go get 无法直接下载,需要配置 GOPRIVATE 环境变量,例如:

shell
go env -w GOPRIVATE="*.my-corp.com"

可以通过 go help module-private 查看更多信息。

通常,该配置需要结合 Git 的另外一个配置来使用:

shell
git config --global url."git@git.my-corp.com:".insteadOf "https://git.my-corp.com/"

即,在下载私有模块的时候默认使用 git 协议。

3. string 和 []byte 互相转换

最简单的:

go
package main

import (
	"fmt"
)

func main() {
	s := "hello"
	fmt.Println([]byte(s))
	// [104 101 108 108 111]

	b := []byte{'h', 'e', 'l', 'l', 'o'}
	fmt.Println(string(b))
	// hello
}

另外一种无须额外分配内存的方式为:

go
package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

// StringToBytes converts string to byte slice without a memory allocation.
func StringToBytes(s string) (b []byte) {
	sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
	return b
}

// BytesToString converts byte slice to string without a memory allocation.
func BytesToString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func main() {
	s := "hello"
	fmt.Println(StringToBytes(s))
	// [104 101 108 108 111]

	b := []byte{'h', 'e', 'l', 'l', 'o'}
	fmt.Println(BytesToString(b))
	// hello
}

参考 https://github.com/gin-gonic/gin/blob/master/internal/bytesconv/bytesconv.go

4. recover 错误处理

go
package main

import (
	"errors"
	"fmt"
)

func main() {
	defer func() {
		if r := recover(); r != nil {
			var err error

			switch x := r.(type) {
			case string:
				err = errors.New(x)
			case error:
				err = x
			default:
				err = errors.New("unknow error")
			}

			fmt.Printf("err: %v", err)
		}
	}()

	panic("hello")
}

更多内容参考 Defer, Panic, and Recover

5. 自定义 JSON 序列化方法

例如如下配置:

json
{
	"env": "test",
	"interval": "10s"
}

希望能够和如下 struct 对应:

go
type Config struct {
	Env      string        `json:"env"`
	Interval time.Duration `json:"interval"`
}

比较不优雅的做法是手动去将 interval 在 string 和 time.Duration 类型之间进行转换,但是这样实在不方便,更好的做法是自定义序列化方法。参考如下:

go
package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type Duration time.Duration

func (d Duration) MarshalJSON() ([]byte, error) {
	s := fmt.Sprintf("%q", time.Duration(d).String())
	return []byte(s), nil
}

func (d *Duration) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	t, err := time.ParseDuration(s)
	if err != nil {
		return err
	}

	*d = Duration(t)
	return nil
}

type Config struct {
	Env      string   `json:"env"`
	Interval Duration `json:"interval"`
}

func main() {
	c := Config{
		Env:      "test",
		Interval: Duration(time.Second * 10),
	}
	b, _ := json.Marshal(c)
	fmt.Println(string(b))
	// {"env":"test","interval":"10s"}

	s := `{"env":"dev","interval":"2m"}`
	var conf Config
	json.Unmarshal([]byte(s), &conf)
	fmt.Println(conf)
	// {dev 120000000000}
}

6. 一些常用模块