8、使用“nil” Slices and Maps
在一个nil的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。
正确代码:
package main
func main() {
var s []int
s = append(s, 1)
}
错误代码:
package main
func main() {
var m map[string]int
m["one"] = 1
}
运行错误:
panic: assignment to entry in nil map
9、Map的容量
你可以在map创建时指定它的容量,但你无法在map上使用cap()函数。
错误代码:
package main
func main() {
m := make(map[string]int, 99)
cap(m)
}
编译错误:
./main.go:5:5: invalid argument m (type map[string]int) for cap
10、字符串不会为nil
这对于经常使用nil分配字符串变量的开发者而言是个需要注意的地方。
package main
func main() {
var x string = nil
if x == nil {
x = "default"
}
}
编译错误:
./main.go:4:6: cannot use nil as type string in assignment
./main.go:5:7: invalid operation: x == nil (mismatched types string and nil)
正确代码:
package main
func main() {
var x string
if x == "" {
x = "default"
}
}
11、Array函数的参数
如果你是一个C或则C++开发者,那么数组对你而言就是指针。当你向函数中传递数组时,函数会参照相同的内存区域,这样它们就可以修改原始的数据。Go中的数组是数值,因此当你向函数中传递数组时,函数会得到原始数组数据的一份复制。如果你打算更新数组的数据,这将会是个问题。
package main
import "fmt"
func main() {
x := [3]int{1, 2, 3}
func(arr [3]int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
}
运行结果:
[7 2 3]
[1 2 3]
如果你需要更新原始数组的数据,你可以使用数组指针类型。
package main
import "fmt"
func main() {
x := [3]int{1, 2, 3}
func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) //prints &[7 2 3]
}(&x)
fmt.Println(x) //prints [7 2 3]
}
运行结果:
&[7 2 3]
[7 2 3]
另一个选择是使用slice。即使你的函数得到了slice变量的一份拷贝,它依旧会参照原始的数据。
package main
import "fmt"
func main() {
x := []int{1, 2, 3}
func(arr []int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [7 2 3]
}
运行结果:
[7 2 3]
[7 2 3]
12、在Slice和Array使用“range”语句时的出现的不希望得到的值
如果你在其他的语言中使用“for-in”或者“foreach”语句时会发生这种情况。Go中的“range”语法不太一样。它会得到两个值:第一个值是元素的索引,而另一个值是元素的数据。
package main
import "fmt"
func main() {
x := []string{"a", "b", "c"}
for v := range x {
fmt.Println(v) //prints 0, 1, 2
}
}
运行结果:
0
1
2
package main
import "fmt"
func main() {
x := []string{"a", "b", "c"}
for _, v := range x {
fmt.Println(v) //prints a, b, c
}
}
运行结果:
a
b
c
13、Slices和Arrays是一维的
看起来Go好像支持多维的Array和Slice,但不是这样的。尽管可以创建数组的数组或者切片的切片。对于依赖于动态多维数组的数值计算应用而言,Go在性能和复杂度上还相距甚远。
你可以使用纯一维数组、“独立”切片的切片,“共享数据”切片的切片来构建动态的多维数组。
如果你使用纯一维的数组,你需要处理索引、边界检查、当数组需要变大时的内存重新分配。使用“独立”slice来创建一个动态的多维数组需要两步。首先,你需要创建一个外部的slice。然后,你需要分配每个内部的slice。内部的slice相互之间独立。你可以增加减少它们,而不会影响其他内部的slice。
package main
func main() {
x := 2
y := 4
table := make([][]int, x)
for i := range table {
table[i] = make([]int, y)
}
}
使用“共享数据”slice的slice来创建一个动态的多维数组需要三步。首先,你需要创建一个用于存放原始数据的数据“容器”。然后,你再创建外部的slice。最后,通过重新切片原始数据slice来初始化各个内部的slice。
package main
import "fmt"
func main() {
h, w := 2, 4
raw := make([]int, h*w)
for i := range raw {
raw[i] = i
}
fmt.Println(raw, &raw[4])
//prints: [0 1 2 3 4 5 6 7] <ptr_addr_x>
table := make([][]int, h)
for i := range table {
table[i] = raw[i*w : i*w+w]
}
fmt.Println(table, &table[1][0])
//prints: [[0 1 2 3] [4 5 6 7]] <ptr_addr_x>
}
运行结果:
[0 1 2 3 4 5 6 7] 0xc4200140a0
[[0 1 2 3] [4 5 6 7]] 0xc4200140a0
关于多维array和slice已经有了专门申请,但现在看起来这是个低优先级的特性。
14、访问不存在的 Map Keys
这对于那些希望得到“nil”标示符的开发者而言是个技巧(和其他语言中做的一样)。如果对应的数据类型的“零值”是“nil”,那返回的值将会是“nil”,但对于其他的数据类型是不一样的。检测对应的“零值”可以用于确定map中的记录是否存在,但这并不总是可信(比如,如果在二值的map中“零值”是false,这时你要怎么做)。检测给定map中的记录是否存在的最可信的方法是,通过map的访问操作,检查第二个返回的值。
package main
import "fmt"
func main() {
x := map[string]string{"one": "a", "two": "", "three": "c"}
if v := x["two"]; v == "" {
fmt.Println("no entry")
}
}运行结果:
no entry
package main
import "fmt"
func main() {
x := map[string]string{"one": "a", "two": "", "three": "c"}
if _, ok := x["two"]; !ok {
fmt.Println("no entry")
} else {
fmt.Println("exist")
}
}
运行结果:
exist