为什么要给返回参数命名?

huangapple go评论84阅读模式
英文:

Why would return parameters be named?

问题

从给函数的返回参数命名中获得的好处是什么?

英文:

What benefits arise from naming a function's return parameter(s)?

func namedReturn(i int) (ret int) {
    ret = i
    i += 2
    return
}

func anonReturn(i int) int {
    ret := i
    i += 2
    return ret
}

答案1

得分: 71

有一些命名它们的好处:

  • 它作为文档。
  • 它们是自动声明并初始化为零值。
  • 如果你有多个返回点,如果你改变函数的返回值,你不需要改变它们所有,因为它只会说“返回”。

当然也有缺点,主要是很容易通过声明同名变量而意外地隐藏它们。

Effective Go 在命名结果参数上有一节:

Go 函数的返回或结果“参数”可以被赋予名称并像传入参数一样使用作为普通变量。当命名时,它们在函数开始时被初始化为它们类型的零值;如果函数执行一个没有参数的返回语句,结果参数的当前值将作为返回值使用。

名称不是强制的,但它们可以使代码更简洁和清晰:它们是文档。如果我们给 nextInt 的结果命名,很明显返回的 int 是哪个。

func nextInt(b []byte, pos int) (value, nextPos int) {

[...]

英文:

There are some benefits to naming them:

  • It serves as documentation.
  • They are auto-declared and initialized to the zero values.
  • If you have multiple return sites, you don't need to change them all if you change the function's return values since it will just say "return".

There are also downsides, mainly that it's easy to accidentally shadow them by declaring a variable of the same name.

<br/>

Effective Go has a section on named result parameters:

> The return or result "parameters" of a Go function can be given names
> and used as regular variables, just like the incoming parameters. When
> named, they are initialized to the zero values for their types when
> the function begins; if the function executes a return statement with
> no arguments, the current values of the result parameters are used as
> the returned values.
>
> The names are not mandatory but they can make code shorter and
> clearer: they're documentation. If we name the results of nextInt it
> becomes obvious which returned int is which.
>
> func nextInt(b []byte, pos int) (value, nextPos int) {
> [...]

答案2

得分: 28

另一个命名返回变量的特殊用途是被延迟函数字面量捕获。一个简单的示例:

package main

import (
    "errors"
    "fmt"
)

func main() {
    fmt.Println(f())
}

var harmlessError = errors.New("你应该担心!")

func f() (err error) {
    defer func() {
        if err == harmlessError {
            err = nil
        }
    }()
    return harmlessError
}

输出结果是<nil>。在更实际的场景中,延迟函数可以处理恐慌,并且可以修改除错误结果之外的其他返回值。然而,共同的魔法是,延迟字面量有机会在f被正常或通过恐慌终止后修改f的返回值。

英文:

Another special use for a named return variable is to be captured by a deferred function literal. A trivial illustration:

package main

import (
	&quot;errors&quot;
	&quot;fmt&quot;
)

func main() {
	fmt.Println(f())
}

var harmlessError = errors.New(&quot;you should worry!&quot;)

func f() (err error) {
	defer func() {
		if err == harmlessError {
			err = nil
		}
	}()
	return harmlessError
}

Output is &lt;nil&gt;. In more practical scenarios, the deferred function may handle panics, and may modify other return values besides an error result. The magic in common though, is that the deferred literal has a chance to modify the return values of f after f is terminated, either normally or by panic.

答案3

得分: 5

在至少两种情况下,它是有用的:

  1. 每当你必须声明要返回的变量时。例如:

     func someFunc() (int, error) {
         var r int
         var e error
         ok := someOtherFunc(&r)  // 虽然有点牵强,但我承认
         if !ok {
             return r, someError()
         }
         return r, nil
     }
    

    相对于:

     func someFunc() (r int, e error) {
         ok := someOtherFunc(&r)
         if !ok {
             e = someError()
         }
         return
     }
    

    随着函数中执行路径的增加,这变得更加重要。

  2. 当你在文档中记录返回值并希望通过名称引用它们时。godoc 将返回变量视为函数签名的一部分。

英文:

It's useful in at least two cases:

  1. Whenever you have to declare variables that you're going to return. E.g.

     func someFunc() (int, error) {
         var r int
         var e error
         ok := someOtherFunc(&amp;r)  // contrived, I admit
         if !ok {
             return r, someError()
         }
         return r, nil
     }
    

    vs.

     func someFunc() (r int, e error) {
         ok := someOtherFunc(&amp;r)
         if !ok {
             e = someError()
         }
         return
     }
    

    This gets more important as the number of execution paths through the function increases.

  2. When you're documenting return values and want to refer to them by name. godoc considers the return variables part of a function's signature.

答案4

得分: 2

例如,命名返回参数可以通过名称访问。

func foo() (a, b, c T) {
        // ...
        if qux {
                b = bar()
        }
        // ...
        return
}

没有命名返回参数很难复制。一个人必须引入本质上与命名返回参数相同功能的局部变量:

func foo() (T, T, T) {
        var a, b, c T
        // ...
        if qux {
                b = bar()
        }
        // ...
        return a, b, c
}

所以直接允许这样做更容易。

此外,它们也可以在另一个方向上访问:

func foo() (a, b, c T) {
        // ...
        if a > c {
                b = bar()
        }
        // ...
        return
}

等等。

英文:

For example, named return parameters are accessible by, well, name.

func foo() (a, b, c T) {
        // ...
        if qux {
                b = bar()
        }
        // ...
        return
}

This is not easy to replicate w/o named return parameters. One would have to introduce local variables of essentially the same functionality as named return parameters:

func foo() (T, T, T) {
        var a, b, c T
        // ...
        if qux {
                b = bar()
        }
        // ...
        return a, b, c
}

So it's easier to allow that directly.

Additionally, they are accessible also in the other direction:

func foo() (a, b, c T) {
        // ...
        if a &gt; c {
                b = bar()
        }
        // ...
        return
}

Etc.

huangapple
  • 本文由 发表于 2013年2月26日 20:57:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/15089726.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定