好奇的探索者,理性的思考者,踏实的行动者。
Table of Contents:
// $GOROOT/src/time/sleep.go
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := &Timer{
C: c,
r: runtimeTimer{
when: when(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
结构体类型
// $GOROOT/src/sync/pool.go
type poolLocal struct {
private interface{}
shared []interface{}
Mutex
pad [128]byte
}
在代码段中,我们在 poolLocal 这个结构体类型中嵌入了类型 Mutex,这就使得 poolLocal 这个类型具有了互斥同步的能力,我们可以通过 poolLocal 类型的变量,直接调用 Mutex 类型的方法 Lock 或 Unlock。
接口类型
// $GOROOT/src/io/io.go
type ReadWriter interface {
Reader
Writer
}
这里,标准库通过嵌入接口类型的方式来实现接口行为的聚合,组成大接口,这种方式在标准库中尤为常用。
if statement; condition {
// do something
}
statement中
m := make(map[string]int)
v := m["k"]
按照以上写法,当m中不存在k这个key时,v的值是自身类型对应的“零值”。如int类型的零值为0,string类型的零值为"",slice、map、func、channel和指针类型的零值为nil。
// 使用“comma ok”的惯用法来判断key是否在map中
m := make(map[string]int)
if v, ok := m["key1"]; ok {
// "key1"在map中
}
不使用comma ok的类型断言:
i := 10
var a interface{} = i
s := a.(string)
以上实例中a的实际类型为int,无法通过类型断言转换为string,所以会因断言失败而panic。
comma ok语法的方式:
s, ok := a.(string)
Go语言的channel被设计为能够用作“事件通知”,具体实现就是在close之后可以无限receive,不会阻塞并且得到的值都是对应元素类型的“零值”。
首先看一下普通的channel receive语法:
ch := make(chan int)
i := <-ch
如果以上示例中i等于0,我们无法判断是通道ch中确实传递过来一个0;还是因为ch已经被关闭,所以我们得到一个“零值”。
comma ok语法:
// 如果因为ch关闭而得到一个“零值”,ok会是false。
i, ok := <-ch
对于有缓冲channel,如果在其被关闭后缓冲中还有数据,此时comma ok得到的ok会是true,直到缓冲数据读尽变成false:
Go 语言惯用法,是使用 error 这个接口类型表示错误,并且按惯例,我们通常将 error 类型返回值放在返回值列表的末尾,这样在调用函数时,我们可以通过简单的判断返回值列表的最后一个值是否为 nil 来判断函数是否执行成功。
Go 社区流传一个经验法则:“接受接口,返回结构体(Accept interfaces, return structs)”
type Cond struct {
... ...
L Locker
}
func NewCond(l Locker) *Cond {
return &Cond{L: l}
}
Option 写法,顾名思义,就是将所有可选的参数作为一个可选方式,一般我们会设计一个“函数类型”来代表这个 Option,然后配套将所有可选字段设计为一个这个函数类型的具体实现。在具体的使用的时候,使用可变字段的方式来控制有多少个函数类型会被执行。
// 普通写法
package newdemo
type Foo struct {
name string
id int
age int
db interface{}
}
func NewFoo(name string, id int, age int, db interface{}) *Foo {
return &Foo{
name: name,
id: id,
age: age,
db: db,
}
}
//使用Option的写法
type Foo struct {
name string
id int
age int
db interface{}
}
// FooOption 代表可选参数
type FooOption func(foo *Foo)
// WithName 代表Name为可选参数
func WithName(name string) FooOption {
return func(foo *Foo) {
foo.name = name
}
}
// WithAge 代表age为可选参数
func WithAge(age int) FooOption {
return func(foo *Foo) {
foo.age = age
}
}
// WithDB 代表db为可选参数
func WithDB(db interface{}) FooOption {
return func(foo *Foo) {
foo.db = db
}
}
// NewFoo 代表初始化
func NewFoo(id int, options ...FooOption) *Foo {
foo := &Foo{
name: "default",
id: id,
age: 10,
db: nil,
}
for _, option := range options {
option(foo)
}
return foo
}
XXX()和MustXXX()的函数命名方式,是一种 Go 代码设计技巧。
例如 Go 标准库中regexp包提供的Compile和MustCompile函数。和XXX相比,MustXXX 会在某种情况不满足时 panic。
因此使用MustXXX的开发者看到函数名就会有一个心理预期:使用不当,会造成程序 panic。