Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

variable overwrite behavior in "select" defers from otherwise #739

Closed
petar opened this issue Apr 21, 2010 · 7 comments
Closed

variable overwrite behavior in "select" defers from otherwise #739

petar opened this issue Apr 21, 2010 · 7 comments

Comments

@petar
Copy link
Contributor

petar commented Apr 21, 2010

Before filing a bug, please check whether it has been fixed since
the latest release: run "hg pull -u" and retry what you did to
reproduce the problem.  Thanks.

What steps will reproduce the problem?
Try the program:

package main

import "fmt"

func f(ch chan int) (n int) {
        select {
        case n := <-ch:
                fmt.Printf("n=%d\n",n)
                return
        }
        panic("x")
}

func main() {
        ch := make(chan int)
        go func(){
                ch <- 5
        }()
        fmt.Printf("f=%d\n",f(ch))
}

What is the expected output? What do you see instead?

What you MAY expect is (see discussion below):
n=5
f=5

What you see is:
n=5
f=0

What is your $GOOS?  $GOARCH?
darwin, amd64

Which revision are you using?  (hg identify)
613f36280394+ tip

Please provide any additional information below.

This situation has created sneaky bugs in my programs.
I think that either, the correct behavior should be to
output:
n=5
f=5
Or, when the compiler sees the reassignment of an 
existing variable in "case n := <-ch:", it should output
a compiler error, just like it would do if you had
a clause like "n := 4" in the body of f().
@petar
Copy link
Contributor Author

petar commented Apr 22, 2010

Comment 1:

Notice, the problematic situation described here generalizes cleanly as follows.
The code:
f() (n int) {
  n := 4
  ...
}
is not allowed. Because "the new" n shadows the return parameter n.
On the other hand, the following is allowed:
f() (n int) {
  {
    n := 4
    ...
    return  // MARK
  }
  panic("x")
}
So, it seems that as soon as we are in a sub-block of the function body, the compiler
ceases to check whether locally defined new variables shadow the return parameters.
This creates a very easy spot for programmer confusion at the MARK, because the 
programmer is thinking that the local copy of "n" is what is returned, when in fact
it is not.

@peterGo
Copy link
Contributor

peterGo commented Apr 22, 2010

Comment 2:

Isn't this merely a duplicate of issue #377?
The behavior is documented in The Go Programming Language Specification: Short
variable declarations
http://golang.org/doc/go_spec.html#Short_variable_declarations

@petar
Copy link
Contributor Author

petar commented Apr 22, 2010

Comment 3:

Yes, this issue is an exact duplicate. I also looked at the documentation
which suggests that the current implementation violates the specification:
" ... Redeclaration does not introduce a new variable; it just assigns a new value to
the original.... "
Because this is not what happens when you have shadowing of a variable.

@peterGo
Copy link
Contributor

peterGo commented Apr 22, 2010

Comment 4:

"Unlike regular variable declarations, a short variable declaration may redeclare
variables provided they were originally declared in the same block 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."
In your Comment 1 example, the short variable declaration `n := 4` can't redeclare
the function result variable `n int`; `n int`, the original declaration, is not in
the same block. Also, `n := 4` isn't a multi-variable short declaration. Therefore,
`n := 4` declares a new inner variable, shadowing and hiding the outer variable `n int`.

@petar
Copy link
Contributor Author

petar commented Apr 22, 2010

Comment 5:

Fair enough. The compiler behaves by the spec, but my point is that
the spec seems too convoluted:
(1) According to the spec the following two functions are different, but
according to common-sense programmer's assumption, they shouldn't be:
func f() (n int) {
 n,err := Read(...)
 return
}
and
func f() (n int) {
 {
   n,err := Read(...)
   return
 }
}
(2) I have made the following error over and over again in my code
over the past few months, *despite* having known of this issue at various
times (and forgotten it):
func f() (err os.Error) {
 if _,err := Read(); err != nil {
   return
 }
}
And consequently, in complex programs, this takes forever to debug,
especially because while you are debugging it you still have the wrong
idea in your mind about what this code does, so you eyeball it and
you think, the error is not there.
--P

@rsc
Copy link
Contributor

rsc commented Apr 26, 2010

Comment 6:

It's true that := and named returns don't play nicely with each other.
I tend to avoid named returns in all but the most trivial of functions.

Status changed to Duplicate.

Merged into issue #377.

@gopherbot
Copy link
Contributor

Comment 7 by [email protected]:

Notice how when citing a real-world example, the problem is between "if ... {". Maybe
the problem is really with the implicit blocks instead of the scoping issue.
I miss some knowledge here, but what if the implicit scope where executed in the
parent's scope instead of in it's own ? It seems like it would solve most of the
problems and still allow shallow variables.

@golang golang locked and limited conversation to collaborators Jun 24, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants