tour of go day2
感觉光看tour of go的话3天就能全看完了
向切片批量添加元素
func append(s []T, vs ...T) []T
append
的第一个参数是源切片,然后后面可以跟任意个参数作为追加元素,最后返回拼接完成的切片
一般用法如下s = append(s,1,2,3)
这个append
会动态拓展数组容量
for range
for range
形式的循环可以用来方便的遍历切片或者Map
每次迭代的参数固定为两个值,第一个是当前下标,第二个是当前下标对应的值,如果不写第二个参数就只返回下标
1 | var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} |
Map
Map
用于处理键值对间的映射关系,定义方法为var m map[keyType]valueType
默认未初始化的Map
为nil
,需要用make
初始化后才可对其进行操作
1 | type Vertex struct { |
1 | type Vertex struct { |
make
可以加一个可选参数指定Map
的初始容量
与创建结构体时必须在{}
前加类型名不同,初始化Map
时可以省略结构体类型名
1 | var m = map[string]Vertex{ |
Map
支持直接通过下标访问元素m[key] = elem
elem = m[key]
使用delete
删除元素delete(m, key)
查询元素会返回两个值,分别是查询结果和元素是否存在,如果不存在的话则返回对应的零值和false
elem, ok = m[key]
函数值(类似函数指针)
函数可以作为值传递,函数类型定义为func(argT...) retT
1 | func compute(fn func(float64, float64) float64) float64 { |
函数闭包
go的函数可以作为一个闭包维持函数体外的变量的生命周期,简单地说就是一个函数作为函数指针函数对象储存时,其引用的函数外的变量不会被回收,而是由这个函数维持其生命周期
1 | func adder() func(int) int { |
为类型定义方法
go中没有class
的概念,但是可以为类型添加方法,然后就可以用其他语言类似的方式调用某个类型的方法
1 | type Vertex struct { |
注意到这里在函数名前多了(v Vertex)
,go tour 中称其为receiver
参数,只要带这个参数的函数都会被认为是对应类型的方法,这里的v
在函数中有点类似其他语言中的this
或self
,但注意到这里使用的是深拷贝,调用这种类型的方法并不会修改对象的值
如果将接收者参数改为(v *Vertex)
,这时v是方法所属对象的指针,现在对v做的操作就会修改调用改方法的对象的值了
方法与指针重定向
对于函数而言,如果参数定义为指针,向其传非指针的操作会导致静态检查不通过,反之亦然
但对于方法而言,一个定义接收者为指针的方法可以由指针或非指针调用,反之亦然,这是因为对于指针接收者方法而言,如果传递的不是指针,go会自动将其转换为指针v.method() -> (&v).method
(这符合开发时对指针接收者方法的预期,即应该修改调用者的值),而对于非指针方法,同样会自动进行解引用操作
当然多数情况下最好使用指针接收者,转递指针的效率要比拷贝整个结构体要高得多
接口
接口是一组方法签名,可以作为类型使用,任何实现了接口所要求的所有方法的变量都可以被这个接口类型持有(也就是虚函数那一套)
1 | type Abser interface { |
注意到这里是严格区分指针的非指针方法的,也就是说你只给指针实现了方法是不能把非指针量塞进接口变量里的
接口方法的实现是隐式的,也就是没有implements
关键字,依靠签名匹配方法,也就是说一个方法可能会匹配多个接口
接口变量可以和正常的变量一样使用,其持有其对应变量的值和类型信息
1 | type I interface { |
nil接口
接口的对应的值可能为nil
,即使如此接口仍可以调用其对应值的方法(因为有类型信息),此时需要在对应方法里手动处理nil
的情况,注意到接口本身并不为nil
nil
接口是真的什么信息都没有,此时调用方法就会导致空指针错误
零接口
接口可以什么方法都不定义,此时它可以接受任何类型,也就是类似void*
类型断言
接口可以通过直接赋值来读取值,也可以通过类型断言读取值t := i.(T)
该语句断言接口变量i保存了类型为T的值并赋值给t,此时如果保存的变量类型和T不同则会直接panict, ok := i.(T)
如果加上ok参数,则在类型不同时会返回T对应的零值和false,并不会抛panic
类型选择
类型选择用于处理接口可能存在多种类型的情况
1 | func do(i interface{}) { |
通过将具体的类型名换成关键字type
就可以获取接口底层值得类型,然后就可以写case了,注意到这个写法只能在type swtich
中存在
Stringer
通过实现String()
方法使用Println
打印自定义的类型描述
1 | type Stringer interface { |
1 | type Person struct { |
Error
通过实现Error方法可以定义自己的错误输出
1 | type error interface { |
1 | type ErrNegativeSqrt float64 |
这里在sqrt
的参数为负数时就返回ErrNegativeSqrt
注意到fmt.Sprint
在接受一个实现了error
接口的值时会尝试调用其Error
方法,所以这里不能直接调用fmt.Sprint
,至少要先做一下类型转换,不然会陷入Error -> Sprint -> Error...
的循环
从命令行接受参数
使用os
包,os.Args
是一个包含了所有命令行参数的切片,os.Args[0]
为程序路径
tour of go day2