# Go编程面试题 --- ## **1. defer执行顺序** ```go= package main import ( "fmt" ) func main() { callDeferFunction() } func callDeferFunction() { defer func() { fmt.Println("打印前") }() defer func() { fmt.Println("打印中") }() defer func() { fmt.Println("打印后") }() panic("触发异常") } ``` - **结果输出** ```go= 打印后 打印中 打印前 panic: 触发异常 ``` - **代码解析** ``` panic需要等defer结束后才会向上传递; 出现panic恐慌时候,会先按照defer的后进先出的顺序执行,最后才会执行panic。 ``` ## **2. golang for的使用** - 以下代码有什么问题,说明原因 - 代码原型 ```go= type student struct { Name string Age int } func parseStudent() { m := make(map[string]*student, 0) students := []student{ {Name: "zhou", Age: 24}, {Name: "li", Age: 23}, {Name: "wang", Age: 22}, } for _, value := range students { m[value.Name] = &value } } ``` - 修复后的代码 ```go= package main type student struct { Name string Age int } func parseStudent() { m := make(map[string]*student, 0) students := []student{ {Name: "zhou", Age: 24}, {Name: "li", Age: 23}, {Name: "wang", Age: 22}, } // 错误的方式 for _, value := range students { m[value.Name] = &value // 指向的是同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝 // value.Age += 10 // 也无法修改切片的内容,可自行去掉注释代码看看结果如何 } for k, v := range m { println(k, "=>", v.Name, v.Age) } // 正确的方式 for i := 0; i < len(students); i++ { m[students[i].Name] = &students[i] // students[i].Age += 10 // 可自行去掉注释代码看看结果如何 } for k, v := range m { println(k, "=>", v.Name, v.Age) } } func main() { parseStudent() } ``` - **结果输出** ```go= //原型代码结果 zhou => wang 22 li => wang 22 wang => wang 22 // 修改后代码的结果 zhou => zhou 24 li => li 23 wang => wang 22 ``` - **代码解析** ``` m[value.Name] = &value // 指向的是同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝 ``` ## **3. go执行的随机性和闭包** - 下面的代码会输出什么,并说明原因 ```go= package main import ( "fmt" "runtime" "sync" ) func main() { runtime.GOMAXPROCS(2) // 设置执行时使用CPU的多核 wg := sync.WaitGroup{} wg.Add(20) for i := 0; i < 10; i++ { // i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。 go func() { fmt.Println("A: ", i) // 输出的结果都是10 wg.Done() }() } for i := 0; i < 10; i++ { // i是函数参数,与外部for中的i完全是两个变量。 尾部(i)将发生值拷贝,go func内部指向值拷贝地址。 go func(i int) { fmt.Println("B: ", i) // 从0~9输出(顺序不定) wg.Done() }(i) } wg.Wait() } ``` - **结果输出** ```go= B: 9 A: 10 B: 8 A: 10 A: 10 A: 10 B: 1 A: 10 B: 2 B: 3 B: 4 B: 5 B: 6 B: 7 A: 10 A: 10 A: 10 A: 10 A: 10 B: 0 ``` - **代码解析** ``` 谁也不知道执行后打印的顺序是什么样的,所以只能说是随机数字。 但是A:均为输出10,B:从0~9输出(顺序不定)。 ``` ## **4. go 组合继承** - 下面代码会输出什么? ```go= package main import "fmt" type People struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } type Teacher struct { People } func (t *Teacher) ShowB() { fmt.Println("teachershowB") } func main() { t := Teacher{} t.ShowA() } ``` - **结果输出** ```go= showA showB ``` - **代码解析** ``` 这是Golang的组合模式,可以实现OOP(面向对象编程)的继承。 被组合的类型People所包含的方法虽然升级成了外部类型Teacher这个组合类型的方法(一定要是匿名字段),但它们的方法(ShowA())调用时接受者并没有发生变化。 此时People类型并不知道自己会被什么类型组合,当然也就无法调用方法时去使用未知的组合者Teacher类型的功能。 ``` ## **5. go select随机性** - 下面的代码会输出什么,并说明原因 ```go= package main import ( "fmt" "runtime" ) func main() { runtime.GOMAXPROCS(1) intCh := make(chan int, 1) strCh := make(chan string, 1) intCh <- 1 strCh <- "hi" select { case value := <-intCh: fmt.Println(value) case value := <-strCh: panic(value) } } ``` - **结果输出** ```go= 1 ``` - **代码解析** ``` select会随机选择一个可用通用做收发操作。所以代码是有肯触发异常,也有可能不会。 单个chan如果无缓冲时,将会阻塞。 但结合select可以在多个chan间等待执行。有三点原则: 1. select中只要有一个case能return,则立刻执行。 2. 当如果同一时间有多个case均能return, 则伪随机方式抽取任意一个执行。 3. 如果没有一个case能return, 则可以执行”default”块。 ``` ## **6. defer执行顺序与值传递** - 下面的代码会输出什么,并说明原因 ```go= package main import "fmt" func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { a := 1 b := 2 defer calc("1", a, calc("10", a, b)) // calc("10", 1, 2), calc("1", 1, 3) a = 0 defer calc("2", a, calc("20", a, b)) // calc("20", 0, 2), calc("2", 0, 2) b = 1 } ``` - **结果输出** ```go= 10 1 2 3 20 0 2 2 2 0 2 2 1 1 3 4 ``` - **代码解析** ``` defer 函数内部是常用的函数调用,里面是值传递,所以顺序执行calc("10", 1, 2),calc("20", 0, 2) defer 是后进先出,所以接着执行的是calc("2", 0, 2),calc("1", 1, 3) ``` ## **7. go slice 的make与append** - 下面的代码会输出什么,并说明原因 ```go= package main import "fmt" func main() { s := make([]int, 5) // s := make([]int, 0) // 输出的是[1, 2, 3] s = append(s, 1, 2, 3) fmt.Println(s) } ``` - **结果输出** ```go= [0 0 0 0 0 1 2 3] ``` - **代码解析** ``` make初始化是有默认值的,此处默认值为0 ``` ## **8. go map并发安全1** - 下面的代码有什么问题? ```go= type UserAges struct { ages map[string]int sync.Mutex } func (ua *UserAges) Add(name string, age int) { ua.Lock() defer ua.Unlock() ua.ages[name] = age } func (ua *UserAges) Get(name string) int { if age, ok := ua.ages[name]; ok { fmt.Println(age) return age } return -1 } ``` - **结果输出** ```go= 高并发读写时会出现 fatal error: concurrent map read and map write ``` - **代码解析** ``` 在Go中,如果不对map做同步控制,高并发读写时,会出现fatal级别的错误。 解决的办法有三种 1. 在Get函数内的if之前也加上锁 ua.Lock() defer ua.Unlock() 2. 使用sync.Map并发安全的MAP 3. 使用channel ``` ## **9. go map并发安全2** - 下面的代码有什么问题? - **1. 代码原型** ```go= package main import ( "fmt" "strconv" "testing" "time" ) func main() { var t *testing.T TestNonConcurrentMap(t) } var count = 1000 func TestNonConcurrentMap(t *testing.T) { nonCMap := make(map[string]int, 0) go WriteNonCMap(nonCMap) go ReadNonCMap(nonCMap) // To see the fatal time.Sleep(10 * time.Second) } func WriteNonCMap(nonCMap map[string]int) { for index := 0; index < count; index++ { nonCMap[strconv.Itoa(index)] = index } } func ReadNonCMap(nonCMap map[string]int) { for index := 0; index < count; index++ { fmt.Println(nonCMap[strconv.Itoa(index)]) } } ``` - **结果输出** ```go= 高并发读写时会出现 fatal error: concurrent map read and map write ``` - **2. 使用sync.Map解决对go MAP的并发读写** ```go= package main import ( "fmt" "strconv" "sync" "testing" "time" ) func main() { var t *testing.T TestNonConcurrentMap(t) } var count = 100000 func TestNonConcurrentMap(t *testing.T) { nonCMap := &sync.Map{} go WriteNonCMap(nonCMap) go ReadNonCMap(nonCMap) // never fatal time.Sleep(10 * time.Second) } func WriteNonCMap(nonCMap *sync.Map) { for index := 0; index < count; index++ { nonCMap.Store(strconv.Itoa(index), index) } } func ReadNonCMap(nonCMap *sync.Map) { for index := 0; index < count; index++ { fmt.Println(nonCMap.Load(strconv.Itoa(index))) } } ``` - **结果输出** ```go= 0 true ... 99999 true ``` - **3. 使用channel解决对go并发读写** ```go= package main import ( "fmt" "testing" "time" ) func main() { var t *testing.T TestNonConcurrentMap(t) } var count = 100000 func TestNonConcurrentMap(t *testing.T) { nonCMap := make(chan int, 1000) go WriteNonCMap(nonCMap) go ReadNonCMap(nonCMap) // never fatal time.Sleep(10 * time.Second) } func WriteNonCMap(nonCMap chan int) { for index := 0; index < count; index++ { nonCMap <- index } close(nonCMap) } func ReadNonCMap(nonCMap chan int) { for value := range nonCMap { fmt.Println(value) } } ``` - **结果输出** ```go= 0 ... 99999 ``` - **代码解析** ``` 在Go中,如果不对map做同步控制,高并发读写时,会出现fatal级别的错误。 解决的办法有三种 1. 自己针对map的读写进行加锁操作 2. 使用sync.Map并发安全的MAP 3. 使用channel ``` ## **10. go panic and recover 1** - 下面的代码有什么问题? ```go= package main import ( "fmt" ) func main() { defer func() { if err := recover(); err != nil { fmt.Println("err is ", err) } else { fmt.Println("fatal") } }() defer func() { panic("\r\ndefer panic") }() panic("panic") } ``` - **结果输出** ```go= err is defer panic ``` - **代码解析** ``` panic仅有最后一个可以被revover捕获 触发panic("panic")后顺序执行defer,但是defer中还有一个panic,所以覆盖了之前的panic("panic") ``` ## **11. go panic and recover 2** - 下面的代码有什么问题? ```go= package main import ( "fmt" "reflect" ) func main() { defer func() { if err := recover(); err != nil { fmt.Println("++++") f := err.(func() string) fmt.Println("err is =>", err) fmt.Println("f is =>", f()) fmt.Println("reflect is =>", reflect.TypeOf(err).Kind().String()) } else { fmt.Println("fatal") } }() defer func() { panic(func() string { return "\r\ndefer panic" }) }() panic("panic") } ``` - **结果输出** ```go= ++++ err is => 0x4af6c0 f is => defer panic reflect is => func ``` - **代码解析** ``` panic仅有最后一个可以被revover捕获 触发panic("panic")后顺序执行defer,但是defer中还有一个panic,所以覆盖了之前的panic("panic") ``` ## **12. go 闭包1** - 下面的代码会输出什么?为什么? ```go= package main func test(x int) (func(), func()) { return func() { println(x) x += 10 }, func() { println(x) } } func main() { a, b := test(100) a() b() } ``` - **结果输出** ```go= 100 110 ``` - **代码解析** ``` 闭包引用相同变量 ``` ## **13. go 闭包2** - 下面的代码会输出什么?为什么? ```go= package main func test() []func() { functions := make([]func(), 0) for i := 0; i < 10; i++ { // i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。 functions = append(functions, func() { println(&i, i) }) } return functions } func main() { functions := test() for _, function := range functions { function() } } ``` - **结果输出** ```go= 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 0xc00000a048 10 ``` - **代码解析** ``` 闭包延迟求值 for循环复用局部变量i,每一次放入匿名函数的应用都是同一个变量,地址不变化, 最终该值为遍历的最后一个struct的值拷贝。 ``` ## **14. go 闭包3** - 下面的代码会输出什么?为什么? ```go= package main func test() []func() { functions := make([]func(), 0) for i := 0; i < 10; i++ { value := i functions = append(functions, func() { println(&value, value) }) } return functions } func main() { functions := test() for _, function := range functions { function() } } ``` - **结果输出** ```go= 0xc000050000 0 0xc000050008 1 0xc000050010 2 0xc000050018 3 0xc000050020 4 0xc000050028 5 0xc000050030 6 0xc000050038 7 0xc000050040 8 0xc000050048 9 ``` - **代码解析** ``` 进行一次拷贝,把外部变量修改为内部变量。 ``` ## **15. go slice 2** - 下面的代码会输出什么?为什么? ```go= package main import ( "fmt" ) func main() { arr := []int{1, 2, 3} arr = append(arr, []int{4, 5}...) brr := arr fmt.Println(len(arr), &arr, &arr[0]) fmt.Println(len(brr), &brr, &brr[0]) brr[1] = 4 fmt.Println(len(arr), &arr, &arr[1], arr[1]) fmt.Println(len(brr), &brr, &brr[1], brr[1]) } ``` - **结果输出** ```go= 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 4 3 4 5] 0xc00000c308 4 5 &[1 4 3 4 5] 0xc00000c308 4 ``` - **代码解析** ``` 操作的是同一个slice,如果需要不同的slice可以多次make ``` ## **15. go slice 2** - 下面的代码会输出什么?为什么? ```go= package main import ( "fmt" ) func main() { arr := []int{1, 2, 3} arr = append(arr, []int{4, 5}...) brr := arr fmt.Println(len(arr), &arr, &arr[0]) fmt.Println(len(brr), &brr, &brr[0]) brr[1] = 4 fmt.Println(len(arr), &arr, &arr[1], arr[1]) fmt.Println(len(brr), &brr, &brr[1], brr[1]) } ``` - **结果输出** ```go= 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 4 3 4 5] 0xc00000c308 4 5 &[1 4 3 4 5] 0xc00000c308 4 ``` - **代码解析** ``` 操作的是同一个slice,如果需要不同的slice可以多次make ``` ## **16. go 方法集** - 以下代码能编译过去吗?为什么? ```go= package main import ( "fmt" ) type People interface { Speak(string) string } type Student struct{} func (stu *Student) Speak(think string) (talk string) { if think == "bitch" { talk = "You are a good boy" } else { talk = "hi" } return } func main() { var peo People = Student{} fmt.Println(peo.Speak("bitch")) } ``` - **结果输出** ```go= 修改代码为这个能编译通过 var peo People = &Student{} 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 2 3 4 5] 0xc00000c300 5 &[1 4 3 4 5] 0xc00000c308 4 5 &[1 4 3 4 5] 0xc00000c308 4 ``` - **代码解析** ``` 编译不通过 go的方法集仅仅影响接口实现和方法表达式转化,与通过实例或者指针调用方法无关。 ``` ## **17. go type** - 以下代码能编译过去吗?为什么? ```go= package main func main() { i := GetValue() switch i.(type) { case int: println("int") case string: println("string") case interface{}: println("interface") default: println("unknown") } } func GetValue() int { return 1 } ``` - **结果输出** ```go= 修改代码为这个能编译通过 package main import "reflect" func main() { i := GetValue() switch reflect.TypeOf(i).Kind() { /* const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer ) */ case reflect.Int: println("type is int") case reflect.String: println("type is string") case reflect.Interface: println("type is interface") default: println("unknown type") } } func GetValue() int { return 1 } type is int ``` - **代码解析** ``` 编译失败,因为type只能使用在interface,struct中 ``` ## **18. go defer和函数返回值** - 以下代码能编译过去吗?为什么? ```go= package main func main() { println(deferFunc1(1)) println(deferFunc2(1)) println(deferFunc3(1)) } func deferFunc1(i int) (t int) { t = i defer func() { t += 3 }() return t } func deferFunc2(i int) int { t := i defer func() { t += 3 }() return t } func deferFunc3(i int) (t int) { defer func() { t += i }() return 2 } ``` - **结果输出** ```go= 4 1 3 ``` - **代码解析** ``` 需要明确一点是defer需要在函数结束前执行。 函数返回值名字会在函数起始处被初始化为对应类型的零值并且作用域为整个函数 DeferFunc1有函数返回值t作用域为整个函数,在return之前defer会被执行,所以t会被修改,返回4; DeferFunc2函数中t的作用域为函数,返回1; DeferFunc3返回3 ``` ## **19. go struct cmp** - 以下代码能编译过去吗?为什么? - 原型代码 ```go= package main import "fmt" func main() { sn1 := struct { age int name string }{age: 11, name: "qq"} sn2 := struct { age int name string }{age: 11, name: "qq"} if sn1 == sn2 { fmt.Println("sn1== sn2") } sm1 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} sm2 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} if sm1 == sm2 { fmt.Println("sm1== sm2") } } ``` - **结果输出** ```go= invalid operation: sm1 == sm2 (struct containing map[string]string cannot be compared) ``` - 优化后的代码 ```go= package main import ( "fmt" "reflect" ) func main() { sn1 := struct { age int name string }{age: 11, name: "qq"} sn2 := struct { age int name string }{age: 11, name: "qq"} if sn1 == sn2 { fmt.Println("sn1 == sn2") } sm1 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} sm2 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} fmt.Println(reflect.DeepEqual(sn1, sn2)) fmt.Println(reflect.DeepEqual(sn1, sm1)) fmt.Println(reflect.DeepEqual(sn1, sm2)) fmt.Println(reflect.DeepEqual(sn2, sm1)) fmt.Println(reflect.DeepEqual(sn2, sm2)) fmt.Println(reflect.DeepEqual(sm1, sm2)) } ``` - **结果输出** ```go= sn1 == sn2 true false false false false true ``` - **代码解析** ``` 1. 进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。 2. 如果结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice等。 3. 如果该结构属性都是可以比较的,那么就可以使用“==”进行比较操作。 ``` ## **20. go interface 内部结构 1** - 以下代码能编译过去吗?为什么? ```go= package main import ( "fmt" ) type People interface { Show() } type Student struct{} func (stu *Student) Show() {} func live() People { // 这里实现了具体的方法集 var stu *Student return stu } func main() { if live() == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } } /* // 他们的底层结构如下: type eface struct { //空接口 _type *_type //类型信息 data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*) } type _type struct { size uintptr // type size ptrdata uintptr // size of memory prefix holding all pointers hash uint32 // hash of type; avoids computation in hash tables tflag tflag // extra type information flags align uint8 // alignment of variable with this type fieldalign uint8 // alignment of struct field with this type kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero } type iface struct { //带有方法的接口 tab *itab //存储type信息还有结构实现方法的集合 data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*) } type itab struct { inter *interfacetype //接口类型 _type *_type //结构类型 link *itab bad int32 inhash int32 // has this itab been added to hash? fun [1]uintptr // variable sized } */ ``` - **结果输出** ```go= BBBBBBB ``` - **代码解析** ``` interface 内部结构 可以看出iface比eface中间多了一层itab结构。 itab存储_type信息和[]fun方法集 从上面的结构我们就可得出,因为data指向了nil,并不代表interface是nil,所以返回值并不为空. 这里的fun(方法集)定义了接口的接收规则,在编译的过程中需要验证是否实现接口。 go中的接口分为两种: 1. 一种是空的接口 var in interface{} 2. 具体方法集 type People interface { Show() } ``` ## **21. go interface 内部结构 2** - 以下代码能编译过去吗?为什么? ```go= package main import ( "fmt" ) func Foo(x interface{}) { if x == nil { fmt.Println("empty interface") return } fmt.Println("non-empty interface") } func main() { var x *int = nil Foo(x) } ``` - **结果输出** ```go= non-empty interface ``` - **代码解析** ``` interface 内部结构 ``` ## **22. go nil** - 以下代码能编译过去吗?为什么? ```go= package main import ( "fmt" ) func GetValue(m map[int]string, id int) (string, bool) { if _, exist := m[id]; exist { return "存在数据", true } return nil, false // return "", false这样修改即可 } func main() { intMap := map[int]string{ 1: "a", 2: "bb", 3: "ccc", } v, err := GetValue(intMap, 3) fmt.Println(v, err) } ``` - **结果输出** ```go= cannot use nil as type string in return argument ``` - **代码解析** ``` 编译失败 nil可以用作interface、function、pointer、map、slice 和 channel 的“空值”。 但是如果不特别指定的话,Go语言不能识别类型,所以会报错。 ``` ## **23. go iota** - 以下代码能编译过去吗?为什么? ```go= package main import ( "fmt" ) const ( x = iota y z = "zz" k p = iota ) func main() { fmt.Println(x, y, z, k, p) } ``` - **结果输出** ```go= 0 1 zz zz 4 ``` - **代码解析** ``` iota is a predeclared identifier representing the untyped integer ordinal number of the current const specification in a (usually parenthesized) const declaration. It is zero-indexed. const iota = 0 // Untyped int. ``` ## **24. go const vs var** - 以下代码能编译过去吗?为什么? ```go= package main const cl = 100 var bl = 123 func main() { println(&bl, bl) println(&cl, cl) } ``` - **结果输出** ```go= cannot take the address of cl ``` - **代码解析** ``` 常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用. ``` ## **25. go goto** - 以下代码能编译过去吗?为什么? - 原型代码 ```go= package main func main() { for i := 0; i < 10; i++ { loop: println(i) } goto loop } ``` - **结果输出** ```go= goto loop jumps into block starting at .\main.go:4:26 ``` - 优化后的代码 ```go= package main func main() { for i := 0; i < 10; i++ { print(i) if i % 4 == 2 { goto loop continue } } loop: println("done") } func print(i int) int { println(i) return i } ``` - **结果输出** ```go= 0 1 2 done ``` - **代码解析** ``` goto不能跳转到其他函数或者内层代码 ``` ## **26. go alais var** - 以下代码能编译过去吗?为什么? ```go= package main import "fmt" func main() { type MyInt1 int type MyInt2 = int // alais var i int = 9 var i1 MyInt1 = i var i2 MyInt2 = i fmt.Println(i1, i2) } ``` - **结果输出** ```go= cannot use i (type int) as type MyInt1 in assignment 修改此代码能运行 var i1 MyInt1 = MyInt1(i) 9 9 ``` - **代码解析** ``` 基于一个类型创建一个新类型,称之为defintion; 基于一个类型创建一个别名,称之为alias。 MyInt1为称之为defintion,虽然底层类型为int类型,但是不能直接赋值,需要强转; MyInt2称之为alias,可以直接赋值。 ``` ## **27. go alais func** - 以下代码能编译过去吗?为什么? ```go= package main import "fmt" type User struct {} type MyUser1 User type MyUser2 = User // alais func (i MyUser1) m1() { fmt.Println("MyUser1.m1") } func (i User) m2() { fmt.Println("User.m2") } func main() { var i1 MyUser1 var i2 MyUser2 i1.m1() i2.m2() //i1.m2() // 因为MyUser1没有定义该方法 } ``` - **结果输出** ```go= MyUser1.m1 User.m2 ``` - **代码解析** ``` 因为MyUser2完全等价于User,所以具有其所有的方法,并且其中一个新增了方法,另外一个也会有。 ``` ## **28. go alais repeat param or func** - 以下代码能编译过去吗?为什么? ```go= package main import "fmt" type T1 struct {} func (t T1) m1() { fmt.Println("T1.m1") } type T2 = T1 type MyStruct struct { T1 T2 } func main() { my := MyStruct{} my.m1() } ``` - **结果输出** ```go= ambiguous selector my.m1 修改为这个可运行 my.T1.m1() my.T2.m1() T1.m1 T1.m1 ``` - **代码解析** ``` 结果不限于方法,字段也也一样;也不限于type alias,type defintion也是一样的. 只要有重复的方法、字段,就会有这种提示,因为不知道该选择哪个. type alias的定义,本质上是一样的类型,只是起了一个别名,源类型怎么用,别名类型也怎么用,保留源类型的所有方法、字段等。 ``` ## **29. go alais repeat** - 以下代码能编译过去吗?为什么? ```go= package main import ( "errors" "fmt" ) var ErrDidNotWork = errors.New("did not work") func DoTheThing(reallyDoIt bool) (err error) { fmt.Println("DoTheThing enter err is", err) if reallyDoIt { result, err := tryTheThing() fmt.Println("DoTheThing inner err is", err) if err != nil || result != "it worked" { err = ErrDidNotWork } } fmt.Println("DoTheThing outer err is", err) return err } func tryTheThing() (string, error) { return "", ErrDidNotWork } func main() { fmt.Println("true err is", DoTheThing(true)) fmt.Println("false err is", DoTheThing(false)) } ``` - **结果输出** ```go= DoTheThing enter err is <nil> DoTheThing inner err is did not work DoTheThing outer err is <nil> true err is <nil> DoTheThing enter err is <nil> DoTheThing outer err is <nil> false err is <nil> ``` - **代码解析** ``` 函数外部的err变量覆盖了整个函数内部的err的值,详情请看日志输出。 ``` ###### tags: `Golang`