- 定长,元素类型相同;
- 函数的参数传递是一个副本拷贝,如果传的是大数组,效率很低;函数内部操作数组参数,仅仅改变函数内的数组;
- 如果函数想改变原数组,需要使用数组指针。函数内部修改数组,都会反映到原数组上。 例如清零函数:
// 缺点是,传递的参数长度只能是32,不能为其他长度的参数。
func zero(ptr *[32]byte){
*ptr = [32]byte{}
}
- VS list
a := [...]int{0,1,2} //list 计算了固定长度
b := []int{0,1,2} //slice 长度不固定
底层是数组,指向数组的指针+len+cap
- 两个slice不能比较==,只有字节型slice
[]byte
可以比较 用bytes.Equal()
自定义slice的比较,比较len,比较每个元素的值
// 以下都是创建slice
make([]T, len)
make([]T, len, cap) // 等同于make([]T, cap)[:len]
//创建无名数组并返回了一个slice,引用的长度不同。
- slice的零值是nil
slice的零值是nil,长度和容量都是0,长度和容量都是0还有如下:
var s []int
s = nil
s = []int{}
s = []int(nil)
- 切片的切割 []
切割是创建新的slice,并指向原来slice底层的数组。优点:效率高;缺点:副作用,修改新的切片数值,会修改旧的切片。 切割的坑:重新切割不会拷贝底层的数组,所以整个数组会一直保留在内存中,直到没有变量去引用它。在极少数情况下,这可能会导致程序把一大整块数据都保留在内存中,而只用到极少的一部分
- copy 使用场景
不同切片间copy数据。 两个切片共享一个底层数组,或许有重叠都可以正确处理。
type I interface {
Get() int
}
首先 interface是一种类型,从它的定义可以看出来用了 type 关键字,更准确的说 interface 是一种具有一组方法的类型,这些方法定义了 interface 的行为。
go 允许不带任何方法的 interface ,这种类型的 interface 叫 empty interface。
如果一个类型实现了一个 interface 中所有方法,我们说类型实现了该 interface,所以所有类型都实现了 empty interface,因为任何一种类型至少实现了 0 个方法。go 没有显式的关键字用来实现 interface,只需要实现 interface 包含的方法即可。
//1
type I interface {
Get() int
Set(int)
}
//2
type S struct {
Age int
}
func(s S) Get()int {
return s.Age
}
func(s *S) Set(age int) {
s.Age = age
}
//3
func f(i I){
i.Set(10)
fmt.Println(i.Get())
}
func main() {
s := S{}
f(&s) //4
}
这段代码在 #1 定义了 interface I,在 #2 用 struct S 实现了 I 定义的两个方法,接着在 #3 定义了一个函数 f 参数类型是 I,S 实现了 I 的两个方法就说 S 是 I 的实现者,执行 f(&s) 就完了一次 interface 类型的使用。
interface 的重要用途就体现在函数 f 的参数中,如果有多种类型实现了某个 interface,这些类型的值都可以直接使用 interface 的变量存储。
s := S{}
var i I //声明 i
i = &s //赋值 s 到 i
fmt.Println(i.Get())
不难看出 interface 的变量中存储的是实现了 interface 的类型的对象值,这种能力是 duck typing。在使用 interface 时不需要显式在 struct 上声明要实现哪个 interface ,只需要实现对应 interface 中的方法即可,go 会自动进行 interface 的检查,并在运行时执行从其他类型到 interface 的自动转换,即使实现了多个 interface,go 也会在使用对应 interface 时实现自动转换,这就是 interface 的魔力所在。
一个 interface 被多种类型实现时,有时候我们需要区分 interface 的变量究竟存储哪种类型的值,go 可以使用 comma, ok 的形式做区分 value, ok := em.(T):em 是 interface 类型的变量,T代表要断言的类型,value 是 interface 变量存储的值,ok 是 bool 类型表示是否为该断言的类型 T。
if t, ok := i.(*S); ok {
fmt.Println("s implements I", t)
}
ok 是 true 表明 i 存储的是 *S 类型的值,false 则不是,这种区分能力叫 Type assertions (类型断言)。
如果需要区分多种类型,可以使用 switch 断言,更简单直接,这种断言方式只能在 switch 语句中使用。
switch t := i.(type) {
case *S:
fmt.Println("i store *S", t)
case *R:
fmt.Println("i store *R", t)
}
interface{} 是一个空的 interface 类型,根据前文的定义:一个类型如果实现了一个 interface 的所有方法就说该类型实现了这个 interface,空的 interface 没有方法,所以可以认为所有的类型都实现了 interface{}。如果定义一个函数参数是 interface{} 类型,这个函数应该可以接受任何类型作为它的参数。
func doSomething(v interface{}){
}
如果函数的参数 v 可以接受任何类型,那么函数被调用时在函数内部 v 是不是表示的是任何类型?并不是,虽然函数的参数可以接受任何类型,并不表示 v 就是任何类型,在函数 doSomething 内部 v 仅仅是一个 interface 类型,之所以函数可以接受任何类型是在 go 执行时传递到函数的任何类型都被自动转换成 interface{}。go 是如何进行转换的,以及 v 存储的值究竟是怎么做到可以接受任何类型的,感兴趣的可以看看 Russ Cox 关于 interface 的实现 。
既然空的 interface 可以接受任何类型的参数,那么一个 interface{}类型的 slice 是不是就可以接受任何类型的 slice ?
func printAll(vals []interface{}) { //1
for _, val := range vals {
fmt.Println(val)
}
}
func main(){
names := []string{"stanley", "david", "oscar"}
printAll(names)
}
上面的代码是按照我们的假设修改的,执行之后竟然会报 cannot use names (type []string) as type []interface {} in argument to printAll 错误,why?
这个错误说明 go 没有帮助我们自动把 slice 转换成 interface{} 类型的 slice,所以出错了。go 不会对 类型是interface{} 的 slice 进行转换。为什么 go 不帮我们自动转换,一开始我也很好奇,最后终于在 go 的 wiki 中找到了答案 https://github.com/golang/go/wiki/InterfaceSlice 大意是 interface{} 会占用两个字长的存储空间,一个是自身的 methods 数据,一个是指向其存储值的指针,也就是 interface 变量存储的值,因而 slice []interface{} 其长度是固定的N2,但是 []T 的长度是Nsizeof(T),两种 slice 实际存储值的大小是有区别的(文中只介绍两种 slice 的不同,至于为什么不能转换猜测可能是 runtime 转换代价比较大)。
但是我们可以手动进行转换来达到我们的目的。
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}
在我们上文的例子中调用 f 是 f(&s) 也就是 S 的指针类型,为什么不能是 f(s) 呢,如果是 s 会有什么问题?改成 f(s) 然后执行代码。
cannot use s (type S) as type I in argument to f:
S does not implement I (Set method has pointer receiver)
这个错误的意思是 S 没有实现 I,哪里出了问题?关键点是 S 中 set 方法的 receiver 是个 pointer *S 。
interface 定义时并没有严格规定实现者的方法 receiver 是个 value receiver 还是 pointer receiver,上面代码中的 S 的 Set receiver 是 pointer,也就是实现 I 的两个方法的 receiver 一个是 value 一个是 pointer,使用 f(s)的形势调用,传递给 f 的是个 s 的一份拷贝,在进行 s 的拷贝到 I 的转换时,s 的拷贝不满足 Set 方法的 receiver 是个 pointer,也就没有实现 I。go 中函数都是按值传递即 passed by value。
那反过来会怎样,如果 receiver 是 value,函数用 pointer 的形式调用?
type I interface {
Get() int
Set(int)
}
type SS struct {
Age int
}
func (s SS) Get() int {
return s.Age
}
func (s SS) Set(age int) {
s.Age = age
}
func f(i I) {
i.Set(10)
fmt.Println(i.Get())
}
func main(){
ss := SS{}
f(&ss) //ponter
f(ss) //value
}
I 的实现者 SS 的方法 receiver 都是 value receiver,执行代码可以看到无论是 pointer 还是 value 都可以正确执行。
导致这一现象的原因是什么?
如果是按 pointer 调用,go 会自动进行转换,因为有了指针总是能得到指针指向的值是什么,如果是 value 调用,go 将无从得知 value 的原始值是什么,因为 value 是份拷贝。go 会把指针进行隐式转换得到 value,但反过来则不行。
go model orm 推荐 gorm