Go101细节
老貘写的Go101,只能说老貘精力真足,这里记录一些细节部分感觉有意思的点。
switch表达式中的值会默认类型确定,可以自己指定
package main func main() { switch 123 { case int64(123): // error: 类型不匹配 case uint32(789): // error: 类型不匹配 } } func main() { switch int64(123) { case 123: case int(123): // error: 类型不匹配 case int64(123): } }
嵌套的defer可以修改函数的返回值,当然不要这么用。
func F() (r int) { defer func() { r = 123 }() return } func main() { fmt.Println(F()) // 123 }
这里主要是退出协程的细节,这里不会在执行输出Java了,因为在这个它协程的退出后,但是不影响输出c,因为main自己是一个独立的协程。
package main import "fmt" import "runtime" func main() { c := make(chan int) go func() { defer func() { c <- 1 }() defer fmt.Println("Go") func() { defer fmt.Println("C") runtime.Goexit() }() fmt.Println("Java") }() //<-c println(<-c) } C Go 1
一些运算符的优先级
package main import "fmt" type T struct { x int y *int } func main() { var t T p := &t.x // <=> p := &(t.x) fmt.Printf("%T\n", p) // *int *p++ // <=> (*p)++ *p-- // <=> (*p)-- t.y = p a := *t.y // <=> *(t.y) fmt.Printf("%T\n", a) // int }
移位运算时,类型判断取决于右边是否是常量
package main func main() { } const M = 2 var _ = 1.0 << M // 编译没问题。1.0将被推断为一个int值。 var N = 2 var _ = 1.0 << N // 编译失败。1.0将被推断为一个float64值。
var _ = 1.0 << 2 // 编译没问题。1.0将被推断为一个int值。
两个指针类型的基类共享相同的底层类型,则可以相互转换
package main type MyInt int64 type Ta *int64 type Tb *MyInt func main() { var a Ta var b Tb //a = Ta(b) // error: 直接转换是不允许的。 // 但是间接转换是允许的。 y := (*MyInt)(b) x := (*int64)(y) a = x // 等价于下一行 a = (*int64)(y) // 等价于下一行 a = (*int64)((*MyInt)(b)) _ = a }
两个零值的地址可能相等也可能不相等,其实在栈上是肯定不相等的,在堆上是可能相等的。
package main import "fmt" func main() { a := struct{}{} b := struct{}{} x := struct{}{} y := struct{}{} m := [10]struct{}{} n := [10]struct{}{} o := [10]struct{}{} p := [10]struct{}{} fmt.Println(&x, &y, &o, &p) // 对于标准编译器1.14版本,x、y、o和p将 // 逃逸到堆上,但是a、b、m和n则开辟在栈上。 fmt.Println(&a == &b) // false fmt.Println(&x == &y) // true fmt.Println(&a == &x) // false fmt.Println(&m == &n) // false fmt.Println(&o == &p) // true fmt.Println(&n == &p) // false }
一个指针类型的基类可以是该指针类型本身,不推荐如此使用。
package main func main() { type P *P var p P p = &p p = **************p }
package main func main() { type S []S type M map[string]M type C chan C type F func(F) F s := S{0:nil} s[0] = s m := M{"Go": nil} m["Go"] = m c := make(C, 3) c <- c; c <- c; c <- c var f F f = func(F)F {return f} _ = s[0][0][0][0][0][0][0][0] _ = m["Go"]["Go"]["Go"]["Go"] <-<-<-c f(f(f(f(f)))) }
无论一个指针值的类型是定义的还是非定义的,如果它的(指针)类型的基类型为一个结构体类型,则我们可以使用此指针值来选择它所引用着的结构体中的字段。 但是,如果此指针的类型为一个定义的类型,则我们不能使用此指针值来选择它所引用着的结构体中的方法。
我们总是不能使用二级以上指针来选择结构体字段和方法。这里尤其注意引用方法。这里用指针理解蛮好理解的,可能对于确定类型的一级指针无法引用基类的方法难理解一点,还是方法绑定的问题。
package main type T struct { x int } func (T) m(){} // T有一个方法m。 type P *T // P为一个定义的一级指针。 type PP *P // PP为一个定义的二级指针。 func main() { var t T var tp = &t var tpp = &tp var p P = tp var pp PP = &p tp.x = 12 // 没问题 p.x = 34 // 没问题 pp.x = 56 // error: 类型PP没有名为x的字段或者方法。 tpp.x = 78 // error: 类型**T没有名为x的字段或者方法。 tp.m() // 没问题,因为类型*T也有一个m方法。 p.m() // error: 类型P没有名为m的字段或者方法。 pp.m() // error: 类型PP没有名为m的字段或者方法。 tpp.m() // error: 类型**T没有名为m的字段或者方法。 }
对于映射我们一般不需要在取item处进行大量的判断,因为不论你的映射是否为空都无所谓,所以个人建议从入口和出口控制而不是中间过程。
func Foo1(m map[string]int) int { if m != nil { return m["foo"] } return 0 } func Foo2(m map[string]int) int { return m["foo"] }
不整理了,进度过慢。
一个没有高级趣味的人。
email:hushui502@gmail.com