zl程序教程

您现在的位置是:首页 >  后端

当前栏目

《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》10-12章笔记

2023-06-13 09:12:47 时间

第10条 使用iota实现枚举常量

Go的const语法提供了“隐式重复前一个非空表达式”的机制,来看下面的代码:

常量定义的后两行没有显式给予初始赋值,Go 编译器将为其隐式使用第一行的表达式,这样上述定义等价于:


iota是Go语言的一个预定义标识符,它表示的是const声明块(包括单行声明)中每个常量所处位置在块中的偏移值(从零开始)。

iota的值和在const块中第几行有关,并不是在哪第一次使用都是0


位于同一行的iota即便出现多次,其值也是一样的:


如果要略过iota = 0,而从iota = 1开始正式定义枚举常量,可以效仿下面的代码:


iota虽然是第一次使用,但它在const块的第二行,所以值为1,而不是0


举一个“反例”:在一些枚举常量名称与其初始值有强烈对应关系的时候,枚举常量会直接使用显式数值作为常量的初始值。这样的情况极其少见,我在Go标准库中仅找到这一处:

一般使用iota


第11条 尽量定义零值可用的类型

11.2 零值可用

Go从诞生以来就一直秉承着尽量保持“零值可用”的理念


切片零值可用


string方法零值可用


锁零值可用


bytes.Buffer零值可用

这是因为bytes.Buffer结构体用于存储数据的字段buf支持零值可用策略的切片类型


Go语言零值可用的理念给内置类型、标准库的使用者带来很多便利。不过Go并非所有类型都是零值可用的,并且零值可用也有一定的限制,比如:在append场景下,零值可用的切片类型不能通过下标形式操作数据:


另外,像map这样的原生类型也没有提供对零值可用的支持


零值可用的类型要注意尽量避免值复制:

我们可以通过指针方式传递类似 Mutex 这样的类型:

这点不理解,这个零值就是个nil,为啥不能赋值给其他变量呢?

一番测试后,搞明白了

type Person struct {
    age     int
    address string
}

func main() {
    var p Person
    fmt.Printf("p:%v\n", p)         // p:{0 }
    fmt.Printf("p:%p\n", &p)        // p:0xc0000ac018
    fmt.Printf("p.age:%v\n", p.age) // p.age:0

    var p1 *Person
    fmt.Printf("p1:%v\n", p1)         // p1:<nil>
    fmt.Printf("p1:%p\n", &p1)        // p1:0xc0000b4020
    fmt.Printf("p1.age:%v\n", p1.age) // 报错
}

未赋值的结构体变量是分配了内存的,不等于nil,并且结构体内的字段都赋予了初始值。

未赋值的指针等于nil,未分配内存。

这就能解释为什么mutex未赋值就能调用自己的Lock方法而不会NPE。

var lock sync.Mutex
lock.Lock()

保持与Go一致的理念,给自定义的类型一个合理的零值,并尽量保持自定义类型的零值可用,这样我们的Go代码会更加符合Go语言的惯用法。


第12条 使用复合字面值作为初值构造器


12.1 结构体复合字面值

一旦该结构体类型增加了一个新的字段,即使是未导出的,这种值构造方式也将导致编译失败,也就是说,应该将

替换为

显然,Go推荐使用field:value的复合字面值形式对struct类型变量进行值构造,这种值构造方式可以降低结构体类型使用者与结构体类型设计者之间的耦合。

可读性更好、增加字段不会编译错误(解耦)、无顺序要求、不容易出错


复合字面值作为结构体值构造器的大量使用,使得即便采用类型零值时我们也会使用字面值构造器形式: s := myStruct{} // 常用 而较少使用new这一个Go预定义的函数来创建结构体变量实例: s := new(myStruct) // 较少使用


12.3 map复合字面值

对于数组/切片类型而言,当元素为复合类型时,可以省去元素复合字面量中的类型,比如:

还有map


对于key或value为指针类型的情况,也可以省略“&T”


对于零值不适用的场景,我们要为变量赋予一定的初值。对于复合类型,我们应该首选Go提供的复合字面值作为初值构造器。对于不同复合类型,我们要记住下面几点: 对于零值不适用的场景,我们要为变量赋予一定的初值。对于复合类型,我们应该首选Go提供的复合字面值作为初值构造器。对于不同复合类型,我们要记住下面几点: 1、使用field:value形式的复合字面值为结构体类型的变量赋初值; 2、在为稀疏元素赋值或让编译器推导数组大小的时候,多使用index:value的形式为数组/切片类型变量赋初值; 3、使用key:value形式的复合字面值为map类型的变量赋初值。(Go1.5版本后,复合字面值中的key和value类型均可以省略不写。)


Post Views: 8