『Go』一些有意思的代码写法

函数的不定参

新亮笔记、gRPC、kratos、progressbar 等项目都大量用到了这个写法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
type Logger struct {
    // ...
}

type Option func(*option)

type option struct {
    level          Level
    fields         map[string]string
    file           io.Writer
    timeLayout     string
    disableConsole bool
}

func NewLogger(opts ...Option) (*Logger, error) {
    logger := new(option)
    for _, f := range opts {
        f(opt)
    }
    // ...
    logger := XXX
    return logger, nil
}

func WithErrorLevel() Option {
	return func(opt *option) {
		opt.level = ErrorLevel
	}
}

func WithField(key, value string) Option {
	return func(opt *option) {
		opt.fields[key] = value
	}
}

使用

1
2
3
logger, err := NewLogger(
    WithField("key", "value"),
)

这样写有一个好处,后续新增其他参数时,也很方便配置。

接口相关

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var _ Student = (*student)(nil) // 一

type Student interface {
    i() // 二
    Study()
}

type student struct {
    Name string
}

第一种

1
var _ Student = (*student)(nil)

强制 *student 去实现 Student 接口,编译器会进行检查。

如果没有实现呢,编译器就会报错:

panic: runtime error: invalid memory address or nil pointer dereference

如果去掉那一行强制检查,就不会报错了。

第二种

1
i()

这也是一种很巧妙的写法,强制 Student 接口中的方法只能在本包中实现,因为有这个首字母小写的方法,其他包是不能实现的。这里的方法名只要是首字母小写就可以。