48

In go there are functions which return two values or more values, commonly one is an error. Suppose that I want to store the first return value into an already initialized variable, but I would like to initialize the variable to contain the error inline. Is there a way to do this?

For example, say I had this code

var a int
//This code doesn't compile because err doesn't exist
a, err = SomeFuncWithTwoReturnValues()
//This code doesn't compile either
a, err := SomeFuncWithTwoReturnValues()

I know you could do this, but I was hoping there was a way to do it all inline

var a int
var err error
a, err = SomeFuncWithTwoReturnValues()

or

a, err := SomeFuncWithTwoReturnValues()

EDIT: The code above actually compiles, so I looked back at my code to drill down more and have created a quick sample that actually replicates the problem (not just in my mind...).

package main

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

func main() {
    a := make([]int, 1)
    a[0], b := myfunc()
    a[0] = b
}

Compiler says main.go|9| non-name a[0] on left side of :=. If I make it = instead of := though then b is never created. I get the feeling that there is not shorthand way to do it though.

9
  • 2
    The second example you provide should actually work. What does the compiler say about it? Commented Aug 1, 2014 at 15:10
  • True, I was getting a compile error in a slightly more complex problem so on the fly transcribed into to condense down the problem. Gonna take a closer look at see if I missed something Commented Aug 1, 2014 at 15:16
  • Added another code sample that actually duplicates the situation more closely Commented Aug 1, 2014 at 15:26
  • 1
    The variable should be err, with error as the type. Don't call it error. Commented Aug 1, 2014 at 15:40
  • 1
    True. It's hard to insert newlines in comments, though. Commented Aug 1, 2014 at 15:51

5 Answers 5

10

As you've mentioned in the comments, you'll need to use the = operator in order to assign to a variable you've already declared. The := operator is used to simultaneously declare and assign a variable. The two are the same:

var x int
x = 5
//is the same as
x := 5

This solution will at least compile:

package main

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

func main() {
    var b int
    a := make([]int, 1)
    a[0], b = myfunc()
    a[0] = b
}

To answer your question, I don't think there is a way to simultaneously use an undeclared and a declared variable when returning multiple values. That would be trying to use two different operators simultaneously.

Edit: just saw your example from the code that compiles, so it appears you're already familiar with go's assignment operators. I'll leave the example up anyway.

Sign up to request clarification or add additional context in comments.

2 Comments

Pretty sure based on playing around/comments just have to declare the variable, no "shortcut" way to do it with the := operator when dealing with an array.
any update? using simultaneously undeclared and declared variable together in assignments.
7

Golang is not a very consistent language. This is a good example. At the beginning I was confused and it would be much simpler if they would always allow the := operator. The compiler is smart enough to detect already declared variables:

package main

import "fmt"

func testFunc() (int,error) {
   return 42,fmt.Errorf("Test Error")
}

func main() {
   number1,err := testFunc() // OK
   number2,err := testFunc() // OK, even if err is already defined
   number1,err = testFunc()  // OK
   // number1,err := testFunc() // ERROR: no new variables on left side of :=

   fmt.Println(number1,number2,err)
}

Playground Link: https://play.golang.org/p/eZVB-kG6RtX

It's not consistent, because golang allows you to use := for already declared variables if you assign to them while also introducing a new variable. So the compiler can detect that variables already exists and skip their declaration. But the golang developers decided to allow that only if you introduce at least one new value. The last example shows that.

Comments

1

I ran into this situation like this:

package main
import "os"

func main() {
   var cache struct { dir string }
   // undefined: err
   cache.dir, err = os.UserCacheDir()
   // non-name cache.dir on left side of :=
   cache.dir, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   println(cache.dir)
}

as you discovered, this issue does not have a clean solution. You can declare an extra variable:

dir, err := os.UserCacheDir()
if err != nil {
   panic(err)
}
cache := userCache{dir}

Or, while more verbose, you can declare the error beforehand. This can save memory, as Go does not use a Rust ownership model:

var (
   cache struct { dir string }
   err error
)
cache.dir, err = os.UserCacheDir()

Comments

0

As mention in the spec, while using:=, if one of the variables is new, then the old one will just be assigned with the new data.

Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // redeclares offset

Comments

0

As mentioned by the other answers you cannot use assignment and declaration in the same return statement. You have to use either.

However I guess the main reason for your question is cleaning up the code so you don't have to declare an extra err variable above the method or function statement.

You can solve this in two ways:

  1. Declare a global var err error variable and use it in the assignment:
var err error

func MyFunc(someInput string) {
  var a int
  a, err = someOtherFunction()
}
  1. If your method or function returns an error you can use the declared return variable
func MyFunc(someInput string) (err error) {
  var a int
  a, err = someOtherFunction()
  return
}

I mainly have the problem in methods when I want to assign something to a struct member, e.g.:

type MyStruct struct {
  so string
}

func (m *MyStruct) SomeMethod() (err error) {
  m.so, err = SomeFunction()
  // handle error and continue or return it
  return
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.