4

When I initialize an error variable globally it seems that it's nil to another function in the same package.
I don't understand why is this code not panicing?

package main

import (
    "os"
    "fmt"
)

var loadErr error

func main() {
    f, loadErr := os.Open("asdasd")
    if loadErr != nil {
        checkErr()
    }
    if f != nil {
        fmt.Println(f.Name())
    }
}

// panic won't be called because loadErr is nil
func checkErr() {
    if loadErr != nil {
        panic(loadErr)
    }
}

But when I do this, it seems to work as expected?

package main

import (
    "os"
)

var loadErr error

func main() {
    _, err := os.Open("asdasd")
    loadErr = err
    if loadErr != nil {
        checkErr()
    }
}

// panic will be called as expected
func checkErr() {
    if loadErr != nil {
        panic(loadErr)
    }
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
J. Gorey
  • 59
  • 1
  • 3

2 Answers2

10
func main() {
    _, loadErr := os.Open("asdasd")

You create a new, local variable loadErr, the global one is never set. Use just =, not :=, to use the global one.

Edit: To hold the second value too, you have to predeclare the second variable:

var f *os.File
f, loadErr = os.Open("asdasd")

Unfortunately, you can't use := here, as := will not consider non-local variables and just create a local variable in this case.

tkausl
  • 13,686
  • 2
  • 33
  • 50
  • I edited my question to be more clear. I eventually must use := because in real life situation I need to hold f as well – J. Gorey Mar 14 '17 at 08:07
  • 1
    @J.Gorey so what is your question? You're creating a local variable that shadows the global one. – zerkms Mar 14 '17 at 08:09
6

The short variable declaration creates new variables if the variables on the left side have been declared in an outer scope. This is also true if not all variables are new on the left side, which otherwise would be redeclaration (which does not introduce a new variable; it just assigns a new value to the original).

So if you want to assign value to the global variable, don't use short variable declaration but simple assignment. Of course, for a tuple assignment where one variable has been declared earlier, you must declare the other one too prior:

var f *os.File
f, loadErr = os.Open("asdasd")
if loadErr != nil {
    // Handle error
}

You can write it more compact:

var f *os.File
if f, loadErr = os.Open("asdasd"); loadErr != nil {
    // Handle error
}

There is also another option using short variable declaration, but then you must "manually" assign the error value to the global loadErr variable:

f, err := os.Open("asdasd")
loadErr = err
if err != nil {
    // Handle error
}

Or more compact:

f, err := os.Open("asdasd")
if loadErr = err; err != nil {
    // Handle error
}

See related / possible duplicate question: How to use global var across files in a package?

Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827