8

I have a Go function which returns two integer values. Below is the function

func temp() (int, int){
 return 1,1
}

Is it possible to put temp function directly into a Println and print both the outputs using string formatting as below:

fmt.Println("first= %d and second = %d", temp() ) // This doesn't work

In Python, I am able to do the following:

def func():
    return 1,1
print("{0}={1}".format(*func())
>> '1=2'

Can I do Something similar in Go too?

rawwar
  • 4,834
  • 9
  • 32
  • 57
  • @maerics, i think asking a basic question for which we can find ready answers can be downvoted. But, i did search for quite sometime and i couldn't find an answer to what i was looking and so, i wrote a question. i do agree with your opinion. and thanks for the answer. – rawwar Oct 04 '18 at 20:17
  • @RayfenWindspear, statically typed? i am sorry but, we can declare a:=1 or a:="Hello" right? – rawwar Oct 04 '18 at 20:19
  • 1
    OR, but not both. := is just syntactic sugar for the compiler to infer the type assigned. – RayfenWindspear Oct 04 '18 at 20:21
  • 2
    Eh, I don't think "multi-value expression destructuring" has anything to do with static typing. But I suppose what's obvious to one person isn't always obvious to someone else, especially neophytes... – maerics Oct 04 '18 at 20:21
  • @RayfenWindspear, thanks. understood – rawwar Oct 04 '18 at 20:22
  • @maerics actually, you are right because `fmt.Println(temp())` actually works in this case. It just isn't able to do the same when the entire set of args isn't directly from the one function. – RayfenWindspear Oct 04 '18 at 20:28
  • 1
    @RayfenWindspear yeah, seems like it was a deliberate choice by the go language designers, not a forced requirement by the strictness of the type system. And thanks for the follow-up, I didn't know that example would work! – maerics Oct 04 '18 at 20:30
  • 1
    @maerics seems it works in the case where a function takes a variadic interface{} as well as when the types match up exactly. TIL. https://play.golang.org/p/Tbja3KNZO_W – RayfenWindspear Oct 04 '18 at 20:33
  • @RayfenWindspear, nice, i think this cannot be formatted into the string, but..i can use it directly?, right – rawwar Oct 04 '18 at 20:35
  • 1
    I found an in-depth article that is worthy of an answer in itself, but it's not valid to answer a question here with just a link, so I'll put it here. https://medium.com/golangspec/multi-valued-expressions-in-go-bd28ddfe1b39 – RayfenWindspear Oct 04 '18 at 20:41
  • 1
    It is irritating, that the page's (tab's, e.g. in Chrome) name starts with python here. The tags might be switched. Since the questions obviously belongs rather to Go than to Python – h0ch5tr4355 Jan 26 '22 at 14:02

3 Answers3

10

Foreword: I released this utility in github.com/icza/gox, see gox.Wrap().


First, for what you attempt to do you should use fmt.Printf() instead of fmt.Println() as only the former expects and uses a format string.

Going forward, this isn't supported by default, because quoting from Spec: Calls:

As a special case, if the return values of a function or method g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(parameters_of_g)) will invoke f after binding the return values of g to the parameters of f in order. The call of f must contain no parameters other than the call of g, and g must have at least one return value. If f has a final ... parameter, it is assigned the return values of g that remain after assignment of regular parameters.

And fmt.Printf() has a signature of:

func Printf(format string, a ...interface{}) (n int, err error)

You cannot pass other parameters to fmt.Printf() besides a function call (the return values of the call).

Note that the signature of fmt.Println() is:

func Println(a ...interface{}) (n int, err error)

Which means that fmt.Println(temp()) works, and so does with any other functions that have at least one return value, because the last sentence of the quoted part allows this ("If f has a final ... parameter, it is assigned the return values of g that remain after assignment of regular parameters.")

But with a little trick we can achieve what you want with fmt.Printf() too.

Note that if temp() would return a value of type []interface{}, we could use ... to pass it as the value of some variadic parameter.

Meaning this works:

func main() {
    fmt.Printf("1: %v, 2: %v\n", temp()...)
}

func temp() []interface{} { return []interface{}{1, 2} }

And it properly prints (try it on the Go Playground):

1: 1, 2: 2

So we just need a utility function that wraps the return values of any function into a []interface{}, and so we can use this to pass to fmt.Printf().

And it's dead-simple:

func wrap(vs ...interface{}) []interface{} {
    return vs
}

As detailed above (with fmt.Println()), we can pass the return values of any function that has at least 1 return value to wrap() as the values of its input parameters.

Now using this wrap() function, see the following example:

func main() {
    fmt.Printf("1: %v\n", wrap(oneInt())...)
    fmt.Printf("1: %v, 2: %v\n", wrap(twoInts())...)
    fmt.Printf("1: %v, 2: %v, 3: %v\n", wrap(threeStrings())...)
}

func oneInt() int { return 1 }

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

func threeStrings() (string, string, string) { return "1", "2", "3" }

This works, and it outputs (try it on the Go Playground):

1: 1
1: 1, 2: 2
1: 1, 2: 2, 3: 3

For more on the topic, see related question:

Multiple values in single-value context

Return map like 'ok' in Golang on normal functions

icza
  • 389,944
  • 63
  • 907
  • 827
6

No, this is not possible. You must assign all values from a multi-value expression to separate variables to use them, e.g.:

a, b := temp()
fmt.Println("first = %d and second = %d", a, b)
// first = 1 and second = 1

[Edit]

Interestingly, it appears that in some cases you can use multi-value expressions as function call arguments if the argument types and arity match, or for purely variadic functions (Go Playground):

func oneTwo() (int, int) {
  return 1, 2
}

func incr2(x, y int) (int, int) {
  return x + 1, y + 1
}

func main() {
  incr2(oneTwo()) // OK: multi-value return and arguments match.

  fmt.Println(oneTwo()) // OK: pure variadic function.

  fmt.Printf("%d %d", oneTwo()) // ERR: mixed formal and variadic args.
}
maerics
  • 151,642
  • 46
  • 269
  • 291
  • 1
    This is buried in the question comments, but is valid material for being in a more prominent view. Here is an article detailing the specifics of what is and is not possible. https://medium.com/golangspec/multi-valued-expressions-in-go-bd28ddfe1b39 – RayfenWindspear Oct 04 '18 at 20:45
0

I was able to print it when i did not use any string formatting as below

fmt.Println(temp())

This was only supported by Println, but not printf

rawwar
  • 4,834
  • 9
  • 32
  • 57