golang错题集

独角的犀牛 · · 770 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文即Go语言的那些坑三。

不要对Go并发函数的执行时机做任何假设

请看下列的列子:

1import(2"fmt"3"runtime"4"time"5)6func main(){7names := []string{"lily","yoyo","cersei","rose","annei"}8for_, name := range names{9go func(){10fmt.Println(name)11}()12}13runtime.GOMAXPROCS(1)14runtime.Gosched()15}

请问输出什么?

答案:

1annei2annei3annei4annei5annei

为什么呢?是不是有点诧异?

输出的都是“annei”,而“annei”又是“names”的最后一个元素,那么也就是说程序打印出了最后一个元素的值,而name对于匿名函数来讲又是一个外部的值。因此,我们可以做一个推断:虽然每次循环都启用了一个协程,但是这些协程都是引用了外部的变量,当协程创建完毕,再执行打印动作的时候,name的值已经不知道变为啥了,因为主函数协程也在跑,大家并行,但是在此由于names数组长度太小,当协程创建完毕后,主函数循环早已结束,所以,打印出来的都是遍历的names最后的那一个元素“annei”。

如何证实以上的推断呢?

其实很简单,每次循环结束后,停顿一段时间,等待协程打印当前的name便可。

1import(2"fmt"3"runtime"4"time"5)6func main(){7names := []string{"lily","yoyo","cersei","rose","annei"}8for_, name := range names{9go func(){10fmt.Println(name)11}()12time.Sleep(time.Second)13}14runtime.GOMAXPROCS(1)15runtime.Gosched()16}

打印结果:

1lily2yoyo3cersei4rose5annei

以上我们得出一个结论,不要对“go函数”的执行时机做任何的假设,除非你确实能做出让这种假设成为绝对事实的保证。

假设T类型的方法上接收器既有T类型的,又有T指针类型的,那么就不可以在不能寻址的T值上调用T接收器的方法

请看代码,试问能正常编译通过吗?

1import(2"fmt"3)4type Lili struct{5Name string6}7func (Lili *Lili) fmtPointer(){8fmt.Println("poniter")9}10func (Lili Lili) fmtReference(){11fmt.Println("reference")12}13func main(){14li := Lili{}15li.fmtPointer()16}

答案:

1能正常编译通过,并输出"poniter"

感觉有点诧异,请接着看以下的代码,试问能编译通过?

1import(2"fmt"3)4type Lili struct{5Name string6}7func (Lili *Lili) fmtPointer(){8fmt.Println("poniter")9}10func (Lili Lili) fmtReference(){11fmt.Println("reference")12}13func main(){14Lili{}.fmtPointer()15}

答案:

1不能编译通过。2“cannot call pointer method on Lili literal”3“cannot take the addressofLili literal”

是不是有点奇怪?这是为什么呢?其实在第一个代码示例中,main主函数中的“li”是一个变量,li的虽然是类型Lili,但是li是可以寻址的,&li的类型是Lili,因此可以调用Lili的方法。

一个包含nil指针的接口不是nil接口

请看下列代码,试问返回什么

1import(2"bytes"3"fmt"4"io"5)6constdebug =true7func main(){8varbuf *bytes.Buffer9ifdebug{10buf =new(bytes.Buffer)11}12f(buf)13}14func f(out io.Writer){15ifout != nil{16fmt.Println("surprise!")17}18}

答案是输出:surprise。

ok,让我们吧debug开关关掉,及debug的值变为false。那么输出什么呢?是不是什么都不输出?

1import(2"bytes"3"fmt"4"io"5)6constdebug =false7func main(){8varbuf *bytes.Buffer9ifdebug{10buf =new(bytes.Buffer)11}12f(buf)13}14func f(out io.Writer){15ifout != nil{16fmt.Println("surprise!")17}18}

答案是:依然输出surprise。

这是为什么呢?

这就牵扯到一个概念了,是关于接口值的。概念上讲一个接口的值分为两部分:一部分是类型,一部分是类型对应的值,他们分别叫:动态类型和动态值。类型系统是针对编译型语言的,类型是编译期的概念,因此类型不是一个值。

在上述代码中,给f函数的out参数赋了一个*bytes.Buffer的空指针,所以out的动态值是nil。然而它的动态类型是bytes.Buffer,意思是:“A non-nil interface containing a nil pointer”,所以“out!=nil”的结果依然是true。

但是,对于直接的bytes.Buffer类型的判空不会出现此问题。

1import(2"bytes"3"fmt"4)5func main(){6varbuf *bytes.Buffer7ifbuf == nil{8fmt.Println("right")9}10}

还是输出: right

只有 接口指针 传入函数的接口参数时,才会出现以上的坑。

修改起来也很方便,把*bytes.Buffer改为io.Writer就好了。

1import(2"bytes"3"fmt"4"io"5)6constdebug =false7func main(){8varbuf  io.Writer//原来是var buf *bytes.Buffer9ifdebug{10buf =new(bytes.Buffer)11}12f(buf)13}14func f(out io.Writer){15ifout != nil{16fmt.Println("surprise!")17}18}

将map转化为json字符串的时候,json字符串中的顺序和map赋值顺序无关

请看下列代码,请问输出什么?若为json字符串,则json字符串中key的顺序是什么?

1func main() {2params := make(map[string]string)3params["id"] ="1"4params["id1"] ="3"5params["controller"] ="sections"6data, _ := json.Marshal(params)7fmt.Println(string(data))8}

答案:输出{"controller":"sections","id":"1","id1":"3"}

利用Golang自带的json转换包转换,会将map中key的顺序改为字母顺序,而不是map的赋值顺序。map这个结构哪怕利用for range遍历的时候,其中的key也是无序的,可以理解为map就是个无序的结构,和php中的array要区分开来

Json反序列化数字到interface{}类型的值中,默认解析为float64类型

请看以下程序,程序想要输出json数据中整型id加上3的值,请问程序会报错吗?

1func main(){2jsonStr :=`{"id":1058,"name":"RyuGou"}`3varjsonData map[string]interface{}4json.Unmarshal([]byte(jsonStr), &jsonData)5sum :=  jsonData["id"].(int) +36fmt.Println(sum)7}

答案是会报错,输出结果为:

panic:interfaceconversion:interface{}isfloat64, notint

1使用 Golang 解析 JSON 格式数据时,若以 interface{} 接收数据,则会按照下列规则进行解析:

bool,forJSON booleansfloat64,forJSON numbersstring,forJSON strings[]interface{},forJSON arraysmap[string]interface{},forJSON objectsnilforJSONnull

1应该改为:

2go

3func main(){

4 jsonStr := {"id":1058,"name":"RyuGou"}

5 var jsonData map[string]interface{}

6 json.Unmarshal([]byte(jsonStr), &jsonData)

7 sum := int(jsonData["id"].(float64)) + 3

8 fmt.Println(sum)

9}

>>>>阅读全文


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:独角的犀牛

查看原文:golang错题集

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

770 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传