《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》10-12章笔记
第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
相关文章
- Vue3.0实现todolist之跳转路由(常用的push,back,go等方法)
- go 语言版本控制器
- 12.Go语言-方法
- 《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》7-9章笔记
- 【愚公系列】2022年07月 Go教学课程 026-结构体
- Go高性能之方法接收器 - 指针vs值
- go语言后端框架2021_go语言编译器
- Go语言中常见100问题-#11 Not using the functional options pattern
- Go 源码解读|如何用好 errors 库的 errors.Is() 与 errors.As() 方法
- GO 2,我们来了——2018/11/29
- Go——方法和接口
- go: grpc tls 应用一览
- Go常见错误集锦之不正确的初始化slice方式会降低性能
- 基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试 - 1/3
- Go语言——测试与性能
- Go语言——快速使用Markdown解析库
- go的数据类型-复合数据类型-数组(一)
- Go-循环语句-break
- Go-并发编程-goroutine 和 channel(一)
- Go RWMutex:高并发读多写少场景下的性能优化利器
- Go语言for range(键值循环)
- redis分布式锁的go-redis实现方法详解
- Go基础之–结构体和方法详解编程语言
- Go语言使用range复用临时变量
- Go语言方法和接收器
- 便捷使用Go编程连接Redis(go连接redis)
- Go语言查询Oracle数据库实战实例(go语言查询oracle)
- Go语言快速安装Oracle数据库(go语言安装oracle)
- 数据库Go语言实现监控Oracle数据库(go监听oracle)
- 给Oracle数据库添加专业技能,Go大发展(go_db_oracle)
- Redis集群搭建Go的艰难之路(redis集群搭建go)
- javascript:history.go()和History.back()的区别及应用