Skip to content

Latest commit

 

History

History
87 lines (60 loc) · 4.65 KB

6-2-Never-using-named-result-parameters.md

File metadata and controls

87 lines (60 loc) · 4.65 KB

6.2 什么时候使用返回值参数

返回值参数是 Go 中不经常使用的一个选项。本节将了解何时认为使用返回值参数来使我们的 API 更方便阅读。但首先,让我们回顾一下它是如何工作的。

当我们在函数或方法中返回参数时,我们可以为这些参数附加名称并将它们用作常规变量。当一个返回值参数被命名时,它在函数/方法开始时被初始化为零值。此外,使用返回值参数,我们可以调用裸返回语句(不带参数)。在这种情况下,返回值参数的当前值将用作返回值。

这是一个使用返回值参数 b 的示例:

func f(a int) (b int) {
        b = a
        return
}

我们在此示例中附加了返回值参数 a 名称:b。当我们不带参数调用 return 时,它会在这个阶段返回 b 的当前值。

然而,什么时候推荐使用返回值参数?

首先,让我们考虑以下接口,其中包含从给定地址获取坐标的方法:

type locator interface {
        getCoordinates(address string) (float32, float32, error)
}

由于此接口未对外公开,因此文档不是强制性的。光看这段代码,你能猜出这两个 float32 结果是什么吗?也许是纬度和经度,但顺序是什么?根据惯例,纬度不一定是第一个元素。因此,我们必须检查函数内部实现,以了解结果。

在这种情况下,我们可能应该使用返回值参数来简化代码阅读:

type locator interface {
        getCoordinates(address string) (lat, lng float32, err error)
}

有了这个新版本,我们通过查看接口就可以理解方法签名的含义:第一个是纬度,第二个是经度。

现在,让我们来探讨何时在方法实现中使用返回值参数的问题。我们是否也应该使用返回值参数作为实现本身的一部分?

func (l loc) getCoordinates(address string) (
        lat, lng float32, err error) {
        // ...
}

在这种情况下,具有表现力的方法定义也可以帮助代码读者。因此,我们可能也想使用返回值参数。

Note 如果我们需要返回相同类型的多个结果,我们还可以考虑创建一个具有有意义字段名称的临时结构。然而,这并不总是有效的。例如,当满足我们无法更新的现有接口时。

现在,让我们考虑另一个允许在数据库中存储 Customer 类型的函数签名:

func StoreCustomer(customer Customer) (err error) {
        // ...
}

在这里,将 error 参数命名为 err 没有帮助,也无助于读者。在这种情况下,我们应该倾向于不使用返回值参数。

因此,回答何时使用返回值参数取决于上下文。在大多数情况下,如果不清楚它是否使我们的代码更具可读性,我们不应该使用返回值参数。

此外,我们应该注意,在某些情况下,已经初始化返回值参数可能非常方便,即使它们不一定有助于提高可读性。让我们看一下受 io.ReadFull 函数启发的 Effective Go 中提出的以下示例:

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
}

在这个例子中,返回值参数并没有真正增加可读性。但是,由于 nerr 都被初始化为零值,因此实现更短。然而,另一方面,这个功能也可能让读者一见钟情。同样,这是一个找到正确平衡的问题。

关于裸返回(不带参数的返回)的注意事项:它们在短函数中被认为是可以接受的;否则,它们会损害可读性,因为读者必须记住整个函数的输出。同时,我们应该在函数的范围内保持一致:要么只使用裸返回,要么只使用带参数的返回。

那么关于返回值参数的规则是什么?在大多数情况下,在接口定义的上下文中使用返回值参数可以提高可读性,而不会导致任何副作用。然而,在方法实现的上下文中并没有严格的规则。在某些情况下,例如,如果两个参数具有相同的类型,它们还可以提高可读性。在其他情况下,它们也可以用于方便。因此,当有明显的好处时,我们应该谨慎使用返回值参数。

Note不处理延迟错误 中,我们将讨论在延迟调用的上下文中使用返回值参数的另一个用例。

此外,如果我们不够小心,它们可能会导致副作用和意想不到的后果,我们将在下一节中看到。