单值上下文错误中的多值返回变量和多返回函数

I'm trying to return a variable along with a function that returns multiple values.

Consider this contrived example:

func twoInts() (int, int) {
    return 2, 3
}
func threeInts() (int, int, int) {
    return 1, twoInts()
}

Calling threeInts() returns "multiple-value twoInts() in single-value context".

I understand I can do the following

func twoInts() (int, int) {
    return 2, 3
}
func threeInts() (int, int, int) {
    num1, num2 := twoInts()
    return 1, num1, num2
}

but I am trying to understand why the return is a single-value context.

Does Go consider the return to be int, (int, int)? Is there a way to expand the first function return so that it does return int, int, int?

https://golang.org/ref/spec#Return_statements

There are three ways to return values from a function with a result type:

  1. The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.

  2. The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.

  3. The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.

None of the 3 ways allow mixing single-value expressions with a call to a multi-valued function.


"Is there a way to expand the first function return so that it does return int, int, int?" No, there isn't.

The simple answer why it doesn't work is because the spec doesn't allow it. The spec allows you to return multiple values that are the return values of another function call, but then they must match in numbers and must be assignable to each of the results. Spec: Return statement:

The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.

Think of the return statement with values as an assignment, see emphasizes in the above quote, and also:

A "return" statement that specifies results sets the result parameters before any deferred functions are executed.

And assignments also only allow function calls on the right side if the number and types of result parameters matches the list of variables on the left side:

A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call, a channel or map operation, or a type assertion. The number of operands on the left hand side must match the number of values.

[...] In the second form, the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left...

The question "Why isn't allowed what you want?" is somewhat opinion-based. It could be supported along with assignments, but was "ruled out" during the language design phase. This is almost the same thing as passing arguments to a function call. If you want to pass the return values as arguments to another function, it's possible if they match in numbers and types (see Spec: Calls). What you want would require these things to work: a, b, c, d := f(), g() and f(g(), h()). There was a discussion on it, here are Robert Griesemer's words:

Also, you are making the assumption that your suggestion is an "improvement". That is far from certain. To be clear, the current rules were carefully thought out and are deliberate. You can be assured that we thought about your suggestion during the design of the language (between 2007 and 2009). If your suggestion were permitted, what about f(g(), h()) where both g and h return multiple values that happen to match the parameter list of f? Should probably be permitted as well. And then we should also allow things like: "a, b, c, d := f(), g()" otherwise we have another inconsistency. And so forth. Permitting this is of course possible, but it also opens the door for unexpected errors. For instance, the parameter lists of the functions may change in unexpected ways and the code may still work, hiding errors. So the pragmatic solution here is to allow a small set of rules that are reasonably safe, and not to allow everything that might be possible.