分享3个Go编程的小知识

deletelazy · · 877 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

#### 来源:微信公众号《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

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

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

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