前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go常见错误总结

go常见错误总结

作者头像
俊采
发布2018-05-15 12:10:03
6320
发布2018-05-15 12:10:03
举报

26 Mar 2016 go常见错误总结

最近为了学习go语言,花了点时间翻译《the way to go》这本书相关章节:

详见:https://github.com/Unknwon/the-way-to-go_ZH_CN

在翻译过程中学习了一些go常见的错误和陷阱,特此总结一下,以便自己在今后使用go时少犯错误。

1 误用短声明:=导致变量覆盖

例如,下列代码中remember变量在if语句之外永远都是false,因为if语句中误用了短声明:=。重新定义了一个remember,自动覆盖外面的remember。所以在if语句中操作的remember变量和外面定义的remember不是同一个变量,导致remember在if语句之外一直都是false。

var remember bool = false
if something {
    remember := true
}

正确写法应该是:

var remember bool = false
if something {
    remember = true 
}

2 误用字符串操作导致大量的内存开销

go语言中字符串也是不可变的,比如当连接2个字符串:a+=b,尤其在一个循环中进行类似操作时,因为源字符串不可变,会导致大量的内存拷贝,造成内存开销过大。所以一般使用一个字符数组代替字符串,将字符串内容写入一个缓存中,代码如下:

var b bytes.Buffer

for condition {
    b.WriteString(str) // 将字符串str写入缓存buffer
}
    return b.String()

3 误用defer关闭文件

如果在一个for循环内部处理一系列文件,我们希望使用defer确保文件处理完毕后能自动被关闭。代码如下:

for_, file:=range files {
    if f, err = os.Open(file); err != nil {
        return
    }
    defer f.Close()
    f.Process(data)
}

但是,defer在循环结束后没有被执行,所以文件一直没有被关闭。因为defer仅在函数返回时才能自动执行。所以正确的写法应该是:

for_, file:=range files {
    if f, err = os.Open(file); err != nil {
        return
    }
    f.Process(data)
    f.Close()
 }

4 误用new和make

例如错误的使用new初始化一个map,错误使用make创建一个数组等。new和make的使用场景如下:

  • 切片、映射和通道,使用make
  • 数组、结构体和所有的值类型,使用new

5 误用指向切片的指针

在go语言中,切片实际是一个指向数组的指针。所以当我们需要将切片作为一个参数传递给函数时,实际就是传递了一个指针变量,并且在函数内部可以改变该变量,而不是传递一个值拷贝,所以当切片作为参数传递是,不需要解引用切片,即:

  • 正确的做法:

func findBiggest( listOfNumbers []int ) int {}

  • 错误的做法:

func findBiggest( listOfNumbers *[]int ) int {}

6 误用指针指向一个接口类型

例如以下代码,nexter是一个接口类型,并且定义了一个next()方法读取下一字节。函数nextFew将nexter接口作为参数并读取接下来的num个字节,并返回一个切片。但是nextFew2使用一个指向nexter接口类型的指针作为参数传递给函数,编译程序时,系统会给出一个编译错误:n.next undefined (type *nexter has no field or method next) 。所以切记不要使用一个指针指向接口类型。

package main

import (
    “fmt”
)

type nexter interface {
    next() byte
}

func nextFew1(n nexter, num int) []byte {
    var b []byte
    for i:=0; i < num; i++ {
        b[i] = n.next()
    }
    return b
}

func nextFew2(n *nexter, num int) []byte {
    var b []byte
    for i:=0; i < num; i++ {
        b[i] = n.next() // 编译错误:n.next未定义(*nexter类型没有next成员或next方法)
    }
    return b
}

func main() {
    fmt.Println(“Hello World!”)
}

7 误用指针传递值类型参数

当为一个自定义类型定义方法时,如果不想让该方法改变接受者的数据,那么接受者是一个值类型,传递的是一个值拷贝,这里看似造成了内存开销,但其实值类型的内存是在栈上分配的,分配速度快且开销不大。但是如果传递一个指针类型,go编译器在很多情况下会认为需要创建一个对象,并将对象存入堆中,导致额外的内存分配。所以,如果想要方法改变接收者的数据,就在接收者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。

8 误用协程和通道

如果在一个循环内部使用了协程处理某些事务。当使用break、return或者panic跳出一个循环时,很有可能会导致内存溢出,因为此时协程正在处理某事务而被阻塞。因此在实际代码中,除非此处代码并发执行显得非常重要,才使用协程和通道,否则仅需写一个简单的过程式循环即可。

参考

《the way to go》

LEo at 23:11

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.03.26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 26 Mar 2016 go常见错误总结
    • 1 误用短声明:=导致变量覆盖
      • 2 误用字符串操作导致大量的内存开销
        • 3 误用defer关闭文件
          • 4 误用new和make
            • 5 误用指向切片的指针
              • 6 误用指针指向一个接口类型
                • 7 误用指针传递值类型参数
                  • 8 误用协程和通道
                    • 参考
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档