How to access the "shadowed" variable?

1,561 views
Skip to first unread message

Nan Xiao

unread,
Apr 15, 2016, 5:02:46 AM4/15/16
to golang-nuts
Hi all,

In the following program, the global variable "a" will be "shadowed" by local "a":  

import (
"fmt"
)

var a int

func main() {
a := 1
fmt.Println(a)
}

So is there any method to access the "shadowed" variable?

Thanks very much in advance!

Best Regards
Nan Xiao
Message has been deleted

Nan Xiao

unread,
Apr 15, 2016, 5:20:11 AM4/15/16
to Marvin Stenger, golang-nuts
Hi Marvin,

Could you show me an example? Thanks!

Best Regards
Nan Xiao

On Fri, Apr 15, 2016 at 5:18 PM, Marvin Stenger <marvin.s...@gmail.com> wrote:
AFAIK only reflection, but that's kind of ugly.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/9gK-oDHUvbw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marvin Stenger

unread,
Apr 15, 2016, 5:24:33 AM4/15/16
to golang-nuts, marvin.s...@gmail.com
Sry, I think my statement was wrong. I thought reflection would make it possible, but I couldn't find appropriate functions in the package.

Best,
Marvin

Jan Mercl

unread,
Apr 15, 2016, 5:24:42 AM4/15/16
to Nan Xiao, golang-nuts
On Fri, Apr 15, 2016 at 11:02 AM Nan Xiao <xiaona...@gmail.com> wrote:

> So is there any method to access the "shadowed" variable?

AFAICT, no.

--

-j

Marvin Stenger

unread,
Apr 15, 2016, 5:27:36 AM4/15/16
to golang-nuts, marvin.s...@gmail.com
You could save the address before you shadow, though:
https://play.golang.org/p/zLYh2WhlDU

Nan Xiao

unread,
Apr 15, 2016, 5:30:04 AM4/15/16
to Marvin Stenger, golang-nuts
Hi Marvin,

Oh, it is really tricky. So it seems the global "shadow" variables can't be accessed through normal approaches. 

Best Regards
Nan Xiao

On Fri, Apr 15, 2016 at 5:27 PM, Marvin Stenger <marvin.s...@gmail.com> wrote:
You could save the address before you shadow, though:
https://play.golang.org/p/zLYh2WhlDU

--

Konstantin Shaposhnikov

unread,
Apr 15, 2016, 5:50:51 AM4/15/16
to golang-nuts
The best way to access it is not to shadow it in the first place :)

Ain

unread,
Apr 15, 2016, 6:18:28 AM4/15/16
to golang-nuts
reede, 15. aprill 2016 12:50.51 UTC+3 kirjutas Konstantin Shaposhnikov:
The best way to access it is not to shadow it in the first place :)

IMHO the  ability to shadow variables is a bug, not feature. Hope it gets fixed in go 2. IME it only causes trouble, haven't had a problem where this feature would be of help.


ain

Jakob Borg

unread,
Apr 15, 2016, 6:47:01 AM4/15/16
to Ain, golang-nuts
2016-04-15 12:18 GMT+02:00 Ain <ain.v...@gmail.com>:
> IMHO the ability to shadow variables is a bug, not feature. Hope it gets
> fixed in go 2. IME it only causes trouble, haven't had a problem where this
> feature would be of help.

On the contrary, it's an essential feature. Without it, code like

func getSomething(url string) someType {
resp, err := http.Get(url)
...
}

would break as soon as someone does an import "net/url" for some
unrelated purpose, which would be very annoying.

//jb

Konstantin Khomoutov

unread,
Apr 15, 2016, 6:48:45 AM4/15/16
to Ain, golang-nuts
On Fri, 15 Apr 2016 03:18:28 -0700 (PDT)
Ain <ain.v...@gmail.com> wrote:

> > The best way to access it is not to shadow it in the first place :)
>
> IMHO the ability to shadow variables is a bug, not feature. Hope it
> gets fixed in go 2. IME it only causes trouble, haven't had a problem
> where this feature would be of help.

If this causes you trouble you most probably have overlong functions
(or other code blocks introducing new scopes). I mean, each identifier
not only identifies some entity, it tries to give some meaning to the
intended purpose of that entity. That is, it helps the programmer to
build up a correct mental model of what the data and code mean.

So if you end up with two separate entities in different scopes
assigned the same identifier, it might signify there's some problem
with understanding the meaning of both of them.

Ain

unread,
Apr 15, 2016, 7:08:14 AM4/15/16
to golang-nuts, ain.v...@gmail.com
I don't think I have overlong functions, I usually get bitten with something like

foo := 42
if bar {
  foo = doSomething()
  ...
}

then after some refactoring doSomething returns another value, say error

foo := 42
if bar {
  foo, err := doSomething()
  if err != nil {...}
  ...
}

note that I changed "=" to ":=" because the new var err, but now the outer foo isn't the one I assign value to.


ain
 

Ain

unread,
Apr 15, 2016, 7:11:23 AM4/15/16
to golang-nuts, ain.v...@gmail.com
Just make compiler smarter, so it wouldn't break in these cases?
btw is there a situation where package identifier is legal parameter (ie code foo(url) would make sense, given that url is net/url)?

 
ain

chris dollin

unread,
Apr 15, 2016, 7:34:55 AM4/15/16
to Nan Xiao, golang-nuts
On 15 April 2016 at 10:02, Nan Xiao <xiaona...@gmail.com> wrote:
> Hi all,
>
> In the following program, the global variable "a" will be "shadowed" by
> local "a":
>
> import (
> "fmt"
> )
>
> var a int
>
> func main() {
> a := 1
> fmt.Println(a)
> }
>
>
> So is there any method to access the "shadowed" variable?

Yes.

Don't call the variable declared in main `a` if you want to access
the global variable called `a`.

Renaming it is a change local to `main` and so won't dink with
your code elsewhere.

The point about shadowing is that your /local/ names don't
get interfered with by global names.

Chris

--
Chris "allusive" Dollin

Jakob Borg

unread,
Apr 15, 2016, 10:08:42 AM4/15/16
to Ain, golang-nuts
2016-04-15 13:11 GMT+02:00 Ain <ain.v...@gmail.com>:
> reede, 15. aprill 2016 13:47.01 UTC+3 kirjutas Jakob Borg:
>> On the contrary, it's an essential feature. Without it, code like
>>
>> func getSomething(url string) someType {
>> resp, err := http.Get(url)
>> ...
>> }
>>
>> would break as soon as someone does an import "net/url" for some
>> unrelated purpose, which would be very annoying.
>
>
> Just make compiler smarter, so it wouldn't break in these cases?

That's shadowing. :)

> btw is there a situation where package identifier is legal parameter (ie
> code foo(url) would make sense, given that url is net/url)?

It being a parameter wasn't the point. The same goes for any variable,
type, etc. declared anywhere. Breaking code because someone mentions
the same name at an outer scope would make everything rather fragile.
Importing a package is just an example of a common way to add a name
at the outermost scope, and for example "url" is a quite good variable
name as well. Not being able to use it would be sad.

//jb

David DENG

unread,
Apr 15, 2016, 10:48:22 AM4/15/16
to golang-nuts, ain.v...@gmail.com
This is a problem only when foo is used within the "if bar {}" block. Otherwise, the code will not compile.

In that case, you can use a different variable other than "foo" to fetch the value and assign the value to "foo" before leaving the block.

I think shadowing or not is just a trade-off of convenience and strictness.  I was also bit by this somehow but not quite often a real problem was generated.

Ain

unread,
Apr 15, 2016, 3:34:46 PM4/15/16
to golang-nuts


On Friday, April 15, 2016 at 5:08:42 PM UTC+3, Jakob Borg wrote:
> Just make compiler smarter, so it wouldn't break in these cases?

That's shadowing. :)

IMHO bad solution.

 
It being a parameter wasn't the point. The same goes for any variable,
type, etc. declared anywhere. Breaking code because someone mentions
the same name at an outer scope would make everything rather fragile.
Importing a package is just an example of a common way to add a name
at the outermost scope, and for example "url" is a quite good variable
name as well. Not being able to use it would be sad.

Sorry, I don't get your point.
If package exports some name then outside the package it is accessible only via qualified name, "package.name". So when I import an package, it's exported names can't conflict with my local names.
The conflict (shadowing) can only happen in the scope of my own package, where I'm in control of variable names. So I would prefer an error which forces me to use different variable names instead of shadowing. It's not like we are running out of variable names and must thus reuse them...


ain

kennyl...@gmail.com

unread,
Apr 15, 2016, 6:03:20 PM4/15/16
to golang-nuts, ain.v...@gmail.com
In this case, your problem is :=, not shadowing. Had shadowing not been permitted, you would have been forced to rename "foo", which would just make it clearer that it is a different variable. The fix is to declare error up front with "var err error", and to use assignment, not assignment and declaration. The code is very clear on what it does.

I'm growing more and more into being in favor of only having the var declaration syntax. The shorthand is nice, but can be annoying for multiple return values, where you very often just want to declare one side.
 
As for shadowing itself, basically all languages have it. It's not something new for Go. The same happens in C, JavaScript, Python and others. If you ask it to redeclare a variable, it shadows the previous declaration. If you do this often, then maybe you're using very generic identifier names, or is having issues with := (just use var).

Ain

unread,
Apr 16, 2016, 2:22:10 AM4/16/16
to golang-nuts, kennyl...@gmail.com


On Saturday, April 16, 2016 at 1:03:20 AM UTC+3, kennyl...@gmail.com wrote:
On Friday, 15 April 2016 13:08:14 UTC+2, Ain wrote:
I don't think I have overlong functions, I usually get bitten with something like

foo := 42
if bar {
  foo = doSomething()
  ...
}

then after some refactoring doSomething returns another value, say error

foo := 42
if bar {
  foo, err := doSomething()
  if err != nil {...}
  ...
}

note that I changed "=" to ":=" because the new var err, but now the outer foo isn't the one I assign value to.


ain
 


Had shadowing not been permitted, you would have been forced to rename "foo", which would just make it clearer that it is a different variable.

That's exactly what should happen IMO! Allowing to have two variables with the same name adds zero value to the language and only creates confusion. Consider

 
foo := 42
if bar {
  foo = 7
  fmt.Println(foo)
}
fmt.Println(foo)

Now change "=" to ":=" in the if block


foo := 42
if bar {
  foo := 7
  fmt.Println(foo)
}
fmt.Println(foo)

and you get different result. I would say that more often than not this is a bug, user didn't intend to create new variable in the inner scope. So compiler shouldn't allow it - variable names aren't scarce resource, force me to use different name in the inner scope so that it is very clear that these in fact are different variables!
I usually get bitten when I need another value of the function call, ie I have

  foo, _ = do()

and then I need the other return value too, so I change it to

  foo, bar := do()

but since bar is a new variable I also changed = to := The rule that all names at the left side will be new variables would be OK if the shadowing where not allowed - compiler would complain and I would immediatly realise whats the problem. But current behaviour silently changes the meaning of the program which usually means it introduces an bug.



> As for shadowing itself, basically all languages have it. It's not something new for Go. The same happens in C, JavaScript, Python and others. If you ask it to redeclare a variable, it shadows the previous declaration.

But is it actually a useful feature? In which situation you can't choose different name for your variable so that shadowing would save the day?


ain

Kenny Lasse Hoff Levinsen

unread,
Apr 16, 2016, 6:11:23 AM4/16/16
to Ain, golang-nuts
If you did not have shadowing, you'll probably end up with err1, err2, err3, etc. because you had nested structures of "a, err := blah(); if a > 7 { b, err := blub(); ... }".

You  ignored my suggestion of using "var", which makes it clearer. I do not see what you write as an issue due to shadowing, but rather see it as an issue due to the very subtle difference between assign and the declare and assign shorthand. Also note that the compiler complains if you do not use foo further inside you bar branch, as it does not permit you to make unused variables. If you had shadowed something like a package or a something in a higher scope that was a different type that you tried to use later in the shadowed scope, the type system would give you a beating in a back alley for it.

I'd suggest that you start using a linter. These generally give you warnings about shadowing (which generally result in 9000 warnings about shadowing "err", which is what the Atom "go-plus" package spams me with on larger codebases), which would train you to not make declarations when you intended to make an assignment. The compiler will always permit you to write bugs.

Best regards,
Kenny Levinsen

Reply all
Reply to author
Forward
0 new messages