package mainimport "fmt"func main() {fmt.Printf("Hello World\n")}
> Hello World
package mainimport "fmt"func main() {fmt.Printf("Hello World\n")
foo := []int{1,2,3}
}
prog.go:5: foo declared and not used
package mainimport "fmt"func main() {fmt.Printf("Hello World\n")
foo := []int{1,2,3}
for i := range foo {fmt.Printf("Yay\n")}
}
prog.go:6: i declared and not used
package mainimport "fmt"func main() {fmt.Printf("Hello World\n")
foo := []int{1,2,3}
for _ := range foo {fmt.Printf("Yay\n")}
}
prog.go:6: no new variables on left side of :=
package mainimport "fmt"func main() {fmt.Printf("Hello World\n")
foo := []int{1,2,3}
for _ = range foo {fmt.Printf("Yay\n")}
}
> Hello world
Yay
Yay
Yay
> So hypothetically, what minimal
> changes to Go (language, tools) could improve this experience for beginners?
Maybe give more attention to it in the beginner tutorials. And
introduce some tricks. I now commonly use _ when prototyping when I
expect that error:
foo := []int(1, 2, 3}
_ = foo
or:
import "fmt"
_ = fmt.Printf
This totally defeats the purpose of the error, but throw a rock if you
don't do that sometimes. :)
I remember wondering about make() and new() when I was learning. I
still think they should be a single polymorphic built-in, now for
other reasons. But this is a different subject.
-- rodrigo
Either way, I'm concerned this kind of thing is hurting the adoption of Go. I expect people on this list to say "you're just an idiot". But seriously, I think this little thing may be important. So hypothetically, what minimal changes to Go (language, tools) could improve this experience for beginners?
I was told that this discussion happened many times; I don't follow
the list as closely as I'd like so I missed them all. A while ago this
occurred to me about make() vs new(): I had a struct "foo" with some
fields and methods that later I refactored to be a simple map. So
suddenly what was "f := new(foo)" had to be "f := make(foo)" (http://
play.golang.org/p/OcQfM4Y2z5). It is not a common case but I wish this
could be avoided. I know the difference between make and new and that
it won't change; but I still think they should be a single built-in
just because it would taste better. No big deal anyway.
Sorry for the off-topic. ;)
-- rodrigo
> I was told that this discussion happened many times; I don't follow
> the list as closely as I'd like so I missed them all. A while ago this
> occurred to me about make() vs new(): I had a struct "foo" with some
> fields and methods that later I refactored to be a simple map. So
> suddenly what was "f := new(foo)" had to be "f := make(foo)" (http://
> play.golang.org/p/OcQfM4Y2z5). It is not a common case but I wish this
> could be avoided. I know the difference between make and new and that
> it won't change; but I still think they should be a single built-in
> just because it would taste better. No big deal anyway.
If you're expecting the calling code to use the object directly (e.g.
indexing) then the type's underlying type matters, and you're probably
going to have to update more than just the object construction. If
you're wanting to insulate the underlying type identity then you
probably want to add a NewT constructor function.
Dave.
+1 from me for making this a configurable compiler flag. It's pretty much the only thing that I wish was different from the way it is. When I'm prototyping some code or debugging it there are often variables or packages that sometimes I use, and sometimes I don't. Currently I constantly need to do something like commenting them out , rename them to _, or print them just to make my code compile, which always seems a bit silly to me.And it would make it easier for beginners, too :-)
There are two reasons for having no warnings. First, if it's worth complaining about, it's worth fixing in the code. (And if it's not worth fixing, it's not worth mentioning.) Second, having the compiler generate warnings encourages the implementation to warn about weak cases that can make compilation noisy, masking real errors that should be fixed."
> *There are two reasons for having no warnings. First, if it's worth
> complaining about, it's worth fixing in the code. (And if it's not worth
> fixing, it's not worth mentioning.)
There is at least one case where this is not true:
i:= 0
for i := 0; true; i++ {
if something
break
}
fmt.Printf("processed %d\n", i)
Imagine this in a larger function, where the error is not so obvious.
This _is_ something worth complaining - or warning - about, but it's
still valid go code. Those programming mistakes do happen, and not
warning me about it costed me in this particular case about half an hour
to find. Compared to 30s with a warning.
And the bad thing: I know right now it will happen again, and i will
again start searching for the error, at other locations, where it
becomes visible for the first time.
> Second, having the compiler generate
> warnings encourages the implementation to warn about weak cases that can
> make compilation noisy, masking real errors that should be fixed
The implementation could still be modified in a way that no warnings are
generated. When programming in C, i am very happy to get warnings, and i
try to eliminate all of them, so in the end the output is a silent as
with warnings disabled.
Norbert
I think you have proven the case we're talking about. This code
doesn't do what you think it does, the outer i is never incremented
(even once) and the value of the inner one is thrown away when the
loop is completed. This is precisely the kind of error you WANT the
compiler to tell you about.
What you wanted to do was have the outer i declared (as you do) and
then increment that within the for loop, breaking when your condition
is met. No error, fixes a bug in your code, and much clearer what is
going on.
>> Second, having the compiler generate
>> warnings encourages the implementation to warn about weak cases that can
>> make compilation noisy, masking real errors that should be fixed>
>
> The implementation could still be modified in a way that no warnings are
> generated. When programming in C, i am very happy to get warnings, and i try
> to eliminate all of them, so in the end the output is a silent as with
> warnings disabled.
All Go code that compiles is guaranteed to be free from errors and
warnings. Isn't that a better guarantee?
- Jim
>> There is at least one case where this is not true:
>>
>> i:= 0
>>
>> for i := 0; true; i++ {
>> if something
>> break
>> }
>>
>> fmt.Printf("processed %d\n", i)
>>
>>
>> Imagine this in a larger function, where the error is not so obvious. This
>> _is_ something worth complaining - or warning - about, but it's still valid
>> go code. Those programming mistakes do happen, and not warning me about it
>> costed me in this particular case about half an hour to find. Compared to
>> 30s with a warning.
>>
>> And the bad thing: I know right now it will happen again, and i will again
>> start searching for the error, at other locations, where it becomes visible
>> for the first time.
>
> I think you have proven the case we're talking about. This code
> doesn't do what you think it does, the outer i is never incremented
> (even once) and the value of the inner one is thrown away when the
> loop is completed. This is precisely the kind of error you WANT the
> compiler to tell you about.
You mean the compiler generates an error because the inner i is never
used? Ok, the example was then maybe too simple. What about this one here:
var i int
for i := 0; true; i++ {
if i == 10 {
break
}
}
fmt.Printf("processed %d\n", i)
No error, but the program is doing something wrong.
> What you wanted to do was have the outer i declared (as you do) and
> then increment that within the for loop, breaking when your condition
> is met. No error, fixes a bug in your code, and much clearer what is
> going on.
Em, what fixes a bug in my code? I didn't get it.. this was just a
simple example, complex code can hide the error much better.
> All Go code that compiles is guaranteed to be free from errors and
> warnings. Isn't that a better guarantee?
This would be a better guarantee, but this is not the case. Shadowing a
variable IS a warning, go simply doesn't print the warning. So it is
actually not guaranteed to be warning-free.
I guess almost every variable shadowing is a programming error. And the
warning can easily be fixed, just by renaming the inner variable.
Norbert
You mean the compiler generates an error because the inner i is never
used? Ok, the example was then maybe too simple. What about this one here:var i int
for i := 0; true; i++ {
if i == 10 {
break
}
}fmt.Printf("processed %d\n", i)
No error, but the program is doing something wrong.
> The program is not doing anything wrong. It's just doing something else wrt
> what the programmer *probably/perhaps* intended.
And the development tool doesn't help the programmer to avoid the
probable error, it lets him run straight into a debug session (it could
be even worse, if the error shows only under certain circumstances). The
programmer is not even allowed to enable such warnings, because
"There are two reasons for having no warnings. First, if it's worth
complaining about, it's worth fixing in the code. (And if it's not worth
fixing, it's not worth mentioning.)"
Shadowed variables _are_ worth complaining about, because they almost
every time mean something worth fixing. But there are also situations
thinkable where shadowed variables are wanted by the programmer.
Short declarations introduce a new danger of making errors, in declaring
variables where they shouldn't. So this danger should be handled by the
compiler.
> What if the compared value
> be instead of 10, say 1e9 and it's e.g. a (silly but valid) HW delay loop.
What would be wrong with it? If 1e9 is larger than the range of i, the
compiler should produce an error.
> This is not a mechanically decidable problem. From some heuristics it can
> be considered as a place for a warning. But then you have the remaining
> valid cases generate just noise instead of real warnings.
I prefer a compiler who helps me to get fast results over a bureaucratic
compiler who lets me run into traps. One of the first i do with C is to
enable -Wall. Then i improve the code untill there are no warnings left
- and no noise.
> I think if anyone insist on such warnings (the scope variable shadowing is
> just one of the virtually endless possibilities) then an external tool for
> such source code instrumentation is the way to go.
This could be one way, but i can't see what is so bad about a warning
switch in the compiler.
Norbert
I agree.
When I was a kid I used to play with hammers and nails. I've hit my
fingers a few times, but that didn't stop me, I liked what I was
doing. Hurting my fingers was a part of the learning process. Now I
know how to work with wood without hurting myself.
--
Aram Hăvărneanu
This could be one way, but i can't see what is so bad about a warning switch in the compiler.
But unused variables are what happens when you comment out *part* of something that you want to disable. Why stop at part? Just comment it all out.
> One of the goals of the standard Go compiler is speed. As you add new
> checks of legal syntax that my offend some programmers ideas of good code
> you will start to erode that goal.
Unless you're asking for pretty sophisticated checks I'd have thought
it unlikely that the compilation speed would be noticably affected.
Chris
--
Chris "allusive" Dollin
> Changing a variable to no longer be shadowed changes the semantics of the
> code.
>
> Changing an unused variable to no longer exist does not change the
> semantics of the code.
Sure, an unused variable differs from a shadowed variable.
> A shadowed variable can *sometimes* be a bug, but often it
> is intentional.
I can't prove it, but i doubt it is often intentional. I would rather
call it bad programming style, as it confuses the one (or even the
original programmer a year later) who is reading the code.
But i can indeed imagine that there may be cases where shadowed
variables make sense. It's not that i would want to forbid shadowed
variables at all.
> For instance, sometimes people will copy/paste code in from
> somewhere else and they want it to behave the same way without having to go
> through and check all the variable names (try doing this with python!).
This is of course done very easy, the resulting code is not very clean.
Still the outer variables can be renamed, the warnings could be accepted
or the warnings could be disabled.
And going through the copied text and renaming the variables there is
still faster than debugging for half an hour and finding out that a
variable was shadowed. (depends on the size of the copied text)
> But unused variables are what happens when you comment out *part* of
> something that you want to disable. Why stop at part? Just comment it all
> out.
I'm not against the errors with unused variables. This is annoying, but
it's ok.
Norbert
> There is also a program called govet which discovers common mistakes with
> Printf and company format strings.
>
> In this spirit, I think what you are asking for is golint.
If those warnings could be detected with other programs, that would be a
compromise. I would still miss it in the compiler.
Norbert
> Once the gates have been opened I would expect the checking would not only
> increase in complexity but also in number. No one can eat just one...
I don't think there are many warning sources possible. This ":=" thing
is so far the only situation where i'm always thinking "Why did the
compiler not tell me about it?". Other things like "if (a = b) {}" are
not possible in go, anyway..
Norbert
> +1 from me for making this a configurable compiler flag. It's pretty much
> the only thing that I wish was different from the way it is. When I'm
> prototyping some code or debugging it there are often variables or packages
> that sometimes I use, and sometimes I don't.
+1 from me too, for exactly the same reason: prototyping and
debugging.
This would probably be more typical (why use range()?)
for i:=0; i<3; i++ {
fmt.Println("yay")
}
> On a side note, I'd like to see an actual use for a program that does this.
Iterating with range() and discarding index/values, or generating and
printing some string N times?
Because I need something like the latter several times a year.
I'm getting the "variable declared but not used" error in the following function, and I can't see why:func Sqrt(x float64) float64 {var z float64 = 1.0for i := 0; i < 10; i++ {z := (z*z - x)/(2.0 * z)}return z}The error is in the line "z := (z*z - x)/(2.0 * z)". But z certainly is used; it is returned immediately following the loop. The only way this makes sense is if the z inside the loop is different from the z outside the loop, but that would be very odd...
var i intfor i,err := SomeFunc(); err != nil || i >= someValue; i,err = SomeFunc() {// Code}return i
...and why the := operator is weird.
This *should* be a duplicate variable declaration error, if the it wasn't for the := weirdness.
I have a hard time understand people who complain about this.How can it bother you, and turn you off, that you have to write understandable and maintainable code?
If you just want to print out "Yay!" three times, simply:package mainfunc main() {println("Hello world")for _ = range [3]int{} {println("Yay")}}
On a side note, I'd like to see an actual use for a program that does this.
Newcomers to Go needs to see Go for what it is. For me it's a tool that helps me with a lot of things, including becoming somewhat a better programmer, at least it forces me to appreciate the pretty little details that makes me proud of what I do, like a carpenter would appreciate a beautifully crafted piece of wood.And I don't think you're an idiot. :)Best regardsOn Sat, Dec 17, 2011 at 12:19 AM, Brett <bsla...@gmail.com> wrote:
Hey all,I've been trying to learn and use Go more. I wanted to share with you the difficulty I found as a beginner with the specific error "declared and not used". I thought the best way to illustrate how this felt to me is by showing you a beginner's session from the Go playground.Round 1: Run hello worldpackage mainimport "fmt"func main() {fmt.Printf("Hello World\n")}> Hello World
Round 2: Cool; now let me try a nice literalpackage mainimport "fmt"func main() {fmt.Printf("Hello World\n")foo := []int{1,2,3}}prog.go:5: foo declared and not usedRound 3: Doh; well, at least it compiled otherwise; I'll just use it in a loop, that should fix itpackage mainimport "fmt"func main() {fmt.Printf("Hello World\n")foo := []int{1,2,3}for i := range foo {fmt.Printf("Yay\n")}}
prog.go:6: i declared and not usedRound 4: Doh again; searched around a bit and found mention of the blank identifier (http://golang.org/doc/effective_go.html#maps); I'll try thatpackage mainimport "fmt"func main() {fmt.Printf("Hello World\n")foo := []int{1,2,3}for _ := range foo {fmt.Printf("Yay\n")}}
prog.go:6: no new variables on left side of :=Round 5: Doh thricely; I guess I need the other kind of equals sign or somethingpackage mainimport "fmt"func main() {fmt.Printf("Hello World\n")foo := []int{1,2,3}for _ = range foo {fmt.Printf("Yay\n")}}> Hello worldYayYayYayAt last!My first impression was that I was getting a lot of errors for such simple beginner code. This turned me off a lot. As I have continued writing more complex programs, I still find myself grappling with "declared and not used" frequently. I'm not sure why this is the case; maybe I'm a breadth-first coder and build scaffolding too early?Either way, I'm concerned this kind of thing is hurting the adoption of Go. I expect people on this list to say "you're just an idiot". But seriously, I think this little thing may be important. So hypothetically, what minimal changes to Go (language, tools) could improve this experience for beginners?Thanks,-BrettPS: If you agree with me but are embarrassed, feel free to reply privately and I'll keep your identity a secret!