首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go语言中常见100问题-#43-44 Never using named result parameters

Go语言中常见100问题-#43-44 Never using named result parameters

作者头像
数据小冰
发布2022-08-15 15:13:02
发布2022-08-15 15:13:02
34300
代码可运行
举报
文章被收录于专栏:数据小冰数据小冰
运行总次数:0
代码可运行
从不使用命名返回值参数

在Go语言中函数的返回值使用命名参数一般不常用。本小节将讨论在什么情况下使用它使得API更加方便。在讨论之前,我们先来看一下命名返回参数工作原理。

在方法或函数的返回值参数的类型前可以添加参数名,并且它们可以当做普通的变量。当命名一个返回参数时,参数变量在函数/方法的开始被初始化为零值。这样在函数的返回时候直接写return即可,不用在return后面跟上返回值,函数的返回值就是返回参数类型前的变量内容。

代码语言:javascript
代码运行次数:0
运行
复制
func f(a int) (b int) {
        b = a
        return
}

如上,函数f的返回值是命名参数b,当函数返回的时候直接return即可,返回的内容就是b的值。

那命名函返回参数在什么情况下使用呢?

下面的接口包含一个getCoordinates方法,该方法根据输入的地址返回坐标信息

代码语言:javascript
代码运行次数:0
运行
复制
type locator interface {
        getCoordinates(address string) (float32, float32, error)
}

因为上述接口是一个内部接口(locator不可导出),所以说明文档不是必须的。当你读到这里的代码时候,你能猜测各个float32表示的含义吗?或许你猜到是经度、纬度值,但是具体哪个是经度哪个是纬度呢?根据惯例,纬度并不总是第一个参数,所以不得不检查具体的实现。这种情况下,推荐使用命名函数返回参数让代码根据可读性, 代码如下。

代码语言:javascript
代码运行次数:0
运行
复制
type locator interface {
        getCoordinates(address string) (lat, lng float32, err error)
}

上面的是新版本代码,采用有命名的函数返回参数,可以清晰地看到第一个参数表示纬度,第二是经度。

下面讨论什么时候在方法实现中使用命名返回参数的场景。是否应该将命名返回参数作为实现本身的一部分?

代码语言:javascript
代码运行次数:0
运行
复制
func (l loc) getCoordinates(address string) (
        lat, lng float32, err error) {
        // ...
}

在这种情况下,有名返回参数可以帮助读代码的人,我们可以使用命名的参数。

「NOTE,如果一个函数返回同一个类型的多个结果,可以考虑创建一个有具体意义字段名的结构体。然而,这种情况并不是总是可能的,例如当需要实现已有的接口时,它的签名已经固定」

下面的代码将存储customer信息到数据库中,现在来考虑另外一种函数签名。

代码语言:javascript
代码运行次数:0
运行
复制
func StoreCustomer(customer Customer) (err error) {
        // ...
}

上述的返回值err对开发者来说没有什么特别的帮助。在这种情况下,我们倾向于不使用有命名的返回返回参数。

现在给出什么时候该采用有命名函数返回参数的结论,它依赖上下文环境, 在大多数情况下,如果使用有命名函数返回参数不能让代码更具有可读性,我们不应该使用它。

另一个要考虑到的是,在某些情况下,已经初始化的命名函数返回参数可以使得代码处理更方便,即使它们在可读性方面没有什么帮助。下面的代码就是这样一个例子,该代码是Effective Go书中提倡的写法。

代码语言:javascript
代码运行次数:0
运行
复制
func ReadFull(r io.Reader, buf []byte) (n int, err error) {
        for len(buf) > 0 && err == nil {
                var nr int
                nr, err = r.Read(buf)
                n += nr
                buf = buf[nr:]
        }
        return
}

上述代码采用了有名函数返回参数,虽然它没有提升代码的可读性,但是,n和err一开始就初始化了,让代码变得更精简。另一方面,这个函数可能会让读者初看有些困惑,这需要找到一个平衡点。

对于使用有名函数返回参数问题,一个注意项是:在短函数中,它们是比较好接受的,否则,会可读性变差。因为读者记住整个函数的输出。同时,我们在使用时要保持一致,要么直接return不带参数,要么return全部使用带参数的返回。

总结,命名返回参数使用规则如下:

  • 在大多数情况下,在接口中定义的上下文中使用有名参数可以提高代码可读性,而不会产生任何副作用
  • 在方法实现的上下文中,没有严格的规则,例如如果两个参数类型相同的时候,使用有名参数可以提高代码可读性。也可以利用初始化减少代码。

因此,在实际使用中,如果使用有名参数有明显优点时,我们采用有名函数返回参数。

注意命名结果参数的副作用

在前面小节,分析了有名函数返回参数在某些场景很有用的。然而,如果我们不够小心,一开始就初始化返回变量可能会导致微妙的错误,本小节将讨论这些问题。

继续沿用前面小节中给定一个地址返回它的经度和纬度值例子说明,当返回两个float32时,我们将决定使用命名的函数参数来明确显示纬度和经度。该函数首先将验证给定的地址,然后获取坐标。在此期间,它将对输入的上下文进行检查,以确保它没有被取消或者没有超过截止日期。

下面是getCoordinates一种新的实现,这段代码有什么问题吗?

代码语言:javascript
代码运行次数:0
运行
复制
func (l loc) getCoordinates(ctx context.Context, address string) (
        lat, lng float32, err error) {
        isValid := l.validateAddress(address)
        if !isValid {
                return 0, 0, errors.New("invalid address")
        }

        if ctx.Err() != nil {
                return 0, 0, err
        }

        // Get and return coordinates
}

咋一看,没有什么问题。其实是有问题的,重点是 if ctx.Err()!=nil条件的返回值是err. 然而该err却还没有被赋值,它任然是一开始初始化的零值(nil). 因此,这将会return nil。

然而上面的代码是可以编译的,因为err是有名返回参数,一开始就初始化了。如果不是有名返回参数,代码是不会通过编译的,会提示.

代码语言:javascript
代码运行次数:0
运行
复制
Unresolved reference 'err'

一个可能的修改方法是,将ctx.Err()的返回值赋值给err,代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
if err := ctx.Err(); err != nil {
        return 0, 0, err
}

但是上面的代码ctx.Err()函数返回值赋值给定的一个if作用域内的err,这会屏蔽掉有名返回参数中的err. 这打破了既定的规则,我们不应该将直接返回和有名参数返回混在一起使用。记住,使用有名返回参数并不一定意味着直接使用裸返回语句,可以使用有名返回参数使得签名更清晰。另一种处理方法是使用裸返回语句,代码如下

代码语言:javascript
代码运行次数:0
运行
复制
if err = ctx.Err(); err != nil {
        return
}

总结,在某些情况下,例如多次返回同一类型,我们可以使用有名函数返回参数,它可以提高代码的可读性,有时候也可以让处理逻辑更简洁。但是,我们必须要记住,因为参数一开始被初始化为零值,就像本节前面举的例子,它会导致微妙的错误,这些错误在阅读代码的时候是不容易发现的。因此,在使用有名函数返回参数时,我们要格外小心,以避免潜在的副作用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小冰 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从不使用命名返回值参数
  • 注意命名结果参数的副作用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档