-3

I've found a bug in my code

func receive() (err error) {
    if v, err := produce(); err == nil {
        fmt.Println("value: ", v)
    }
    return
}

Error is never returning from this function, but I was absolutely sure it should.

After some testing I understood that err is redeclared in the if statement. More than that - all variables are always redeclared in short variable assignment inside if statement, despite they were declared before.

This is working code

func receive() (err error) {
    v, err := produce()
    if err == nil {
        fmt.Println("value: ", v)
    }
    return
}

Here is an example: https://play.golang.org/p/1AWBsPbLiI1

Seems like if statement

//some code
if <init_statement>; <expression> {}
//more code

is equivalent to

//some code
{
    <init_statement>
    if expression {}
}
//more code

So, my questions:

1) why existing variables are not used

2) why such scoping is not mentioned in documentation/language spec

3) why compiler doesn't say that no one returns a value

porfirion
  • 1,619
  • 1
  • 16
  • 23
  • 1) because you're declaring new ones 2) this is actually interesting - I cannot find one easily as well 3) you return a value - it's a default value for the `error` type - `nil` – zerkms Aug 28 '18 at 09:51

2 Answers2

8

1) Because the language spec says so.

2) It is:

Each "if", "for", and "switch" statement is considered to be in its own implicit block"

3) You did return at the end of your function. Do not use named return values.

Volker
  • 40,468
  • 7
  • 81
  • 87
  • 1
    Seems like named return values are bad idea at all( Now I removed all empty return statements in the end of functions and use explicit returns in all places. The only case where named return values are useful - is defer functions handling the panic and returning error – porfirion Sep 03 '18 at 11:04
2

A compiler may disallow an empty expression list in a "return" statement if a different entity (constant, type, or variable) with the same name as a result parameter is in scope at the place of the return.

Try to return the value inside if scope and you will get to know that the err variable is shadowed inside the if scope.

func receive1() (err error) {
    if v, err := produce(); err != nil {
        fmt.Println("value2: ", v)
        return // err is shadowed during return
    }   
    return
}

Above code will show an error as:

err is shadowed during return

Playground example

while in the second function the err declared in the return statement is assigned with local variable scope:

// err is the return value. It's OK
func receive2() (err error) {
    v, err := produce()
    if err != nil {
        fmt.Println("value2: ", v)
        return
    }
    return
}

Try it on playground

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.

func complexF3() (re float64, im float64) {
    re = 7.0
    im = 4.0
    return
}

Regardless of how they are declared, all the result values are initialized to the zero values for their type upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.

Himanshu
  • 12,071
  • 7
  • 46
  • 61