Go是门简洁、高效的语言,因为在底层封装了许多东西,在go的编写中有许多细节需要我们留意,此文章尽可能通过代码来列举一些go语言中的细节(keng)。
1.
func main() {
slice := []int{0, 1, 2, 3}
m := make(map[int]*int)
for key, val := range slice {
m[key] = &val
}
for k, v := range m {
fmt.Println(k, "->", *v)
}
}
// 0 -> 3
// 1 -> 3
// 2 -> 3
// 3 -> 3
go中传递是值传递,对于for range slice 其实go在底层遍历的是slice的副本。对于变量key和val在遍历中是不会重新创建,也就是例子中只有一个变量key和val,在遍历中因为取了val的指针,遍历结束,val指针指向遍历最终值为3。
2.比较
- go中的slice、map、函数是不可以比较的,除了和
nil
比较。 - 函数和nil比较总返回false
- sliec在var s []int 或 var s []int = nil声明时和nil比较会相等,其余比较一般不相等,map同理
- struct 比较时编译器会根据双方的成员类型(如结构体中有slice类型则不可比较)、成员声明顺序来确定是否可以比较,在运行时根据成员具体值的比较返回比较值
- 数组可以比较,编译器在编译期间会检查两个数组的成员类型(成员类型不能比较则不能比较)和成员数量,在运行时比较各成员的值
3
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
golang中的字符串只读的,不能修改
4. defer
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func main() {
fmt.Println(increaseA()) // 0
fmt.Println(increaseB()) // 1
}
defer延迟执行是go中重要的机制,defer执行在return之前,increaseA返回匿名变量,实际上在return i 时就执行s = i 将s作为函数结果传递出去了。increaseB的函数声明的返回参数和return中的一致,因此执行defer函数时会改变r的值,这里的r都是同一个变量。
5.
var p *int
func foo() (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
//use p
fmt.Println(*p)
}
func main() {
p, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
此段代码会造成运行时错误,原因在于:=
对于不同作用域的相同变量会重新定义,就是main函数中的指针p
会覆盖全局变量p,在执行bar
函数时,此时全局指针p为nil,bar
函数拿不到指针则报错崩溃。
6.
func main() {
data := []int{1,2,3}
i := 0
++i
fmt.Println(data[i++])
}
上述代码会报错,原因在于go中不存在++i
和--i
,对于i++
它是一条语句,并不是一个表达式,这是go语言在自增方面与其他语言的不同。