分享3个Go编程的小知识

前阵子在网上看到一些关于Go比较不错的小知识点,下面总结下分享给你们。面试

new和make的区别

new和make都是Go中用来建立对象用的,new这个关键字在不少编程语言里都有,好比在C++和Java里,均可以用new来建立对象。编程

在Go中,new的做用一样是用来建立对象,好比new(T)将会为T建立对象,并同时将这个对象赋一个零值,而后返回T对象的指针*T,下面咱们演示用3种不一样的办法建立bytes.Buffer的对象,并返回它的指针。后端

// 声明一个变量,而后取它的地址并赋值给指针p
var buf bytes.Buffer
p := &buf

// 使用复合声明的方式一步完成
p := &bytes.Buffer{}

// 使用new也是一步完成
p := new(bytes.Buffer)
复制代码

上面3种建立对象的方法都是等价的。bash

make在平常的编程中也很经常使用,可是它的使用范围仅限于在slice,map,channel中。使用make也是建立对象,好比make(T),但它返回的是对象的值T,回顾下make的用法。微信

// 建立一个长度为0,容量为8的slice
sl := make([]string, 0, 8)

// 建立一个阻塞的channel
ch := make(chan int)

// 建立一个map
m := make(map[string]string)
复制代码

上面使用make建立的对象返回的都是对应的值类型。markdown

总结下new和make的区别:

1. new返回的是T的指针,make返回的是T的值。app

2. make仅能用于建立slice,map,channel。编程语言

变量名不要带有类型

对于变量命名,Go大师Dave Cheney举了个颇有趣比喻:你给变量命名就像给你家的宠物取名同样,名字上不要带上“xx狗”,”xx猫“,由于你们都能知道它是狗仍是猫。函数

因此你的变量名应该是描述变量的内容,而不是描述变量的类型,看看如下的写法。oop

var usersMap map[string]*User
复制代码

usersMap这个变量名看起来还不错,描述的是*User的map映射类型。可是Go是一门静态语言,咱们给一个map取变量名的时候是不须要像动态语言同样,由于怕赋值错误的类型而给它加上类型的,所以这个Map后缀是多余的。

咱们再以这种方式来命名几个变量。

var (
   companiesMap map[string]*Company
   productsMap  map[string]*Products
)
复制代码

如今,咱们已经命名了3个map类型的变量:usersMap,companiesMap,productsMap ,其中它们对应的value值都是不一样的struct类型。当咱们将*User赋值productsMap的时候,这时候编译器是会报错的,不像动态语言那样只能在运行时才会报错。

这种状况下,加上Map后缀并无更好的描述这个变量,反而还只是一个多余的后缀。因此不建议在变量名中带有类型。

一样的在方法命名上也相似。

type Config struct {
    //
}

func (c *Config) WriteConfig(w io.Writer) {
    //
}

// would be better
func (c *Config) Write(w io.Writer) {
    //
}
复制代码

上面代码中,由于WriteConfig是*Config的方法,因此Config后缀也是多余的。

另外,包名最好别占用类型的名字,就像context包里的Context类型,当咱们引入这个context包的时候,只能用相似ctx这种变量名,而不是context。

// 这样命名就很奇怪,并且十分很差看
func WriteLog(context context.Context, message string)

// 只能以这样的形式取名
func WriteLog(ctx context.Context, message string)
复制代码

总结

Go的变量命名宗旨是简洁明了,变量名应该独立于它的类型。

使用命名的返回值捕获panic

想象下你写的代码中,使用到的一个函数会panic可能会panic,并且你还改不了那个函数,像这样:

func pressButton() {
    fmt.Println("I'm Mr. Meeseeks, look at me!!")
    // other stuff then happens, but if Jerry asks to
    // remove 2 strokes from his golf game...
    panic("It's gettin' weird!")
}
复制代码

虽然这个函数会panic,但你仍是不得不使用它,当它panic的时候咱们能够捕获这个错误,就像这样:

func doStuff() error {
    var err error
    // If there is a panic we need to recover in a deferred func
    defer func() {
        if r := recover(); r != nil {
            err = errors.New("the meeseeks went crazy!")
        }
    }()

    pressButton()
    return err
}
复制代码

当pressButton发生panic的时候,咱们会觉得将会返回一个error,但结果是返回一个nil。是由于pressButton发生panic了就直接返回了,并不会走到return err。

想修复这个问题很简单,只需给error起一个变量名就行了。

func doStuff() (err error) {
    // If there is a panic we need to recover in a deferred func
    defer func() {
        if r := recover(); r != nil {
	    err = errors.New("the meeseeks went crazy!")
	}
    }()

    pressButton()
    return err
}
复制代码

参考文献

1.《理解 Go make 和 new 的区别》 sanyuesha.com/2017/07/26/…

2.《Using named return variables to capture panics in Go》 www.calhoun.io/using-named…

3.dave.cheney.net/2019/01/29/…

感谢阅读,欢迎你们指正,留言分享交流~

来源:微信公众号《Go后端干货》

各类Go,后端技术,面试题分享,欢迎关注

相关文章
相关标签/搜索