美文网首页我爱编程
golang中的陷阱

golang中的陷阱

作者: UUID | 来源:发表于2018-05-24 13:48 被阅读27次

写过其他后端语言的人,在写golang的时候,总会遇到一些奇怪的问题,而且很难排查,我个人觉得是因为两个原因,第一个golang基础不扎实,第二个编程思维没有转换过来。下面都是我自己遇到或者想到的容易遇到的点。如果转载的话,请注明出处,因为我会不定期更新。

1、到底是什么类型

这是我自己想的问题,看以下代码,T到底是什么类型?

type A interface{
    Do() error
}
type B interface{
    Do() error
}

type T int
func (t T)Do() error{
    return nil
}

曾经在某个golang群里问过这个问题,当时没有人回答这个问题,也许是太简单了,不屑,或者是不确定。
请认真思考。
下面是我的分析
T到底什么类型?首先万物皆interface,其次,看T的定义,是int的别名,也就是int32的别名,最后,T实现了Do()方法,但是,A 和 B都申明了这个方法,那么,T到底是A类型呢,还是B类型?答案是T既是
A也是B。为什么?因为golang中,只要实现了某个接口的所有方法,那么它就是这种类型。另一个佐证是goland中的实现关系:


Screenshot from 2018-05-24 11-23-52.png Screenshot from 2018-05-24 11-25-13.png

2、range迭代

代码如下,请先思考运行后的结果是什么:

type student struct {
    Name string
    Age int
}

func main (){
    m := make(map[string]*student)
    stus := []student{
        {Name: "zhou", Age: 24},
        {Name: "li", Age: 23},
        {Name: "wang", Age: 22},
    }
    for _, stu := range stus {
        m[stu.Name] = &stu
    }
    for _,stu := range stus{
        fmt.Println(m[stu.Name])
    }
}

是不是觉得应该是:

&{zhou 24}
&{li 23}
&{wang 22}

但,运行之后的结果为:

&{wang 22}
&{wang 22}
&{wang 22}

为什么?首先从短赋值说起

for _, stu := range stus {
    m[stu.Name] = &stu
}
等同与
var stu student
for _, stu = range stus {
    m[stu.Name] = &stu
}

是不是有点头绪了?因为每次m[stu.Name] = &stu 执行的时候,指向都是stu,而每次迭代,stu指向不同的student,最终的结果就是,所有的数据都指向了最后一个student。至于stu.Name 是不用怀疑的,每次迭代都是立即求值。

那么如何解决呢?既然问题是由于stu引起的,那么,每次迭代让指针指向迭代对象就可以解决,引入新变量就可以:

for _, stu := range stus {
        st := stu
    m[st.Name] = &st
}

或者,map的值不存指针,存对象即可:

m := make(map[string]*student)
...
for _, stu := range stus {
    m[stu.Name] = stu
}
...

3、申明类型

这个是我写工具类的时候遇到的,不多说,直接上代码:

var buf bytes.Buffer
...//write data to buf
r, err := http.NewRequest("PUT", uri+"/apis/", buf)

你会发现,最后一行会报错的,看下NewRequest的定义,最后一个参数是io.Reader类型:

func NewRequest(method, url string, body io.Reader) (*Request, error)

func (b *Buffer) Read(p []byte) (n int, err error) 

很明显,bytes.Buffer 实现了io.Reader接口,根据第一个问题得出的结论,Buffer就是io.Reader类型啊,为什么会报错呢?当然,也不能申明buf为io.Reader对吧,因为我们要往buf里面写入数据呢。
当然用Buffer是没错啦,问题在于我们显式申明buf 为bytes.Buffer,那么类型就已经定了,注定不能用在io.Reader参数上了,虽然它确实是实现了io.Reader 的接口。
原因找到了,怎么解决呢?我们可以不用申明buf是Buffer类型:

buf  := new(bytes.Buffer)
...//write data to buf
r, err := http.NewRequest("PUT", uri+"/apis/", buf)

这样就OK了。

相关文章

  • Golang之Defer

    引用 golang defer实现原理 Golang之轻松化解defer的温柔陷阱 Golang中defer、re...

  • golang中的陷阱

    写过其他后端语言的人,在写golang的时候,总会遇到一些奇怪的问题,而且很难排查,我个人觉得是因为两个原因,第一...

  • Golang的WaitGroup陷阱

    sync.WaitGroup是并发环境中,一个相当常用的数据结构,用来等待所有协程的结束,在写代码的时候都是按着例...

  • golang tips

    参考: Go的50度灰:Golang新开发者要注意的陷阱和常见错误 Traps, Gotchas, and Com...

  • golang 50处陷阱

    golang 50处陷阱 1.main()函数后面的大括号不能放在单独的一行 2.如果存在未使用的变量,将导致编译...

  • Golang数组与切片

    Golang数组与切片 数组的声明 Golang中的数组属于静态数组,不允许动态扩展 Golang中数组会默认初始...

  • 知识分享之Golang——golang中快速获取变量类型

    知识分享之Golang——golang中快速获取变量类型 背景 知识分享之Golang篇是我在日常使用Golang...

  • golang基础介绍part-2(interface,gorou

    Golang相关其他分享 golang基础介绍part-1 衔接介绍 part-1中提到了Golang中各种关键字...

  • Golang定时器陷阱

    所谓陷阱,就是它不是你认为的那样,这种认知误差可能让你的软件留下隐藏Bug。刚好Timer就有3个陷阱,我们会讲1...

  • golang map 赋值 修改 陷阱

    无法修改同一个make创建的map,只能修改不同make创建的map

网友评论

    本文标题:golang中的陷阱

    本文链接:https://www.haomeiwen.com/subject/zniwkftx.html