#### 来源:微信公众号《Go后端干货》
#### 各种Go,后端技术,面试题分享,欢迎关注
#### 公众号上排版更好
<img src="https://static.studygolang.com/190508/d20b3d9f2790729effb6cbea985e443d.png" width=300 height=330>
#### 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种创建对象的方法都是等价的。
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创建的对象返回的都是对应的值类型。
##### 总结下new和make的区别:
1. new返回的是T的指针,make返回的是T的值。
2. make仅能用于创建slice,map,channel。
#### 变量名不要带有类型
对于变量命名,Go大师Dave Cheney举了个很有趣比喻:你给变量命名就像给你家的宠物取名一样,名字上不要带上“xx狗”,”xx猫“,因为大家都能知道它是狗还是猫。
所以你的变量名应该是描述变量的内容,而不是描述变量的类型,看看以下的写法。
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
}
```
#### 参考文献
《理解 Go make 和 new 的区别》
https://sanyuesha.com/2017/07/26/go-make-and-new/
《Using named return variables to capture panics in Go》
https://www.calhoun.io/using-named-return-variables-to-capture-panics-in-go/
https://dave.cheney.net/2019/01/29/you-shouldnt-name-your-variables-after-their-types-for-the-same-reason-you-wouldnt-name-your-pets-dog-or-cat
有疑问加站长微信联系(非本文作者)