This code:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
var a bool
var b interface{}
b = true
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
}
Yields this error in the golang playground:
tmp/sandbox791966413/main.go:11:10: a declared and not used
tmp/sandbox791966413/main.go:14:21: a declared and not used
This is confusing because of what we read in the short variable declaration golang docs:
Unlike regular variable declarations, a short variable declaration may
redeclare variables provided they were originally declared earlier in
the same block (or the parameter lists if the block is the function
body) 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.
So, my questions:
Why can't I redeclare the variable in the code snippet above?
Supposing I really can't, what I'd really like to do is find
a way to populate variables with the output of functions while
checking the error in a more concise way. So is there any way
to improve on the following form for getting a value out of an
error-able function?
var A RealType
if newA, err := SomeFunc(); err != nil {
return err
} else {
A = newA
}
This is happening because you are using the short variable declaration in an if initialization statement, which introduces a new scope.
Rather than this, where a is a new variable that shadows the existing one:
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
you could do this, where a is redeclared:
a, ok := b.(bool)
if !ok {
fmt.Println("Problem!")
}
This works because ok is a new variable, so you can redeclare a and the passage you quoted is in effect.
Similarly, your second code snippet could be written as:
A, err := SomeFunc()
if err != nil {
return err
}
You did redeclare the variable, that is the problem. When you redeclare a variable, it is a different variable. This is known as "shadowing".
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
// original a.
var a bool
var b interface{}
b = true
// redeclared a here. This is a a'
if a, ok := b.(bool); !ok {
// a' will last until the end of this block
fmt.Println("Problem!")
}
// a' is gone. So using a here would get the original a.
}
As for your second question. That code looks great. I would probably switch it to if err == nil (and swap the if and else blocks). But that is a style thing.
The error raised by the compiler is saying that both declarations of variable a are not being used.
You're actually declaring a twice, when using short declaration in the if condition you're creating a new scope that shadows the previous declaration of the variable.
The actual problem you're facing is that you're never using the variable value which, in Go, is considered a compile time error.
Regarding your second question, I think that the shortest way to get the value and check the error is to do something similar to this:
func main() {
a, ok := someFunction()
if !ok {
fmt.Println("Problem!")
}
}
Related
This code:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
var a bool
var b interface{}
b = true
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
}
Yields this error in the golang playground:
tmp/sandbox791966413/main.go:11:10: a declared and not used
tmp/sandbox791966413/main.go:14:21: a declared and not used
This is confusing because of what we read in the short variable declaration golang docs:
Unlike regular variable declarations, a short variable declaration may
redeclare variables provided they were originally declared earlier in
the same block (or the parameter lists if the block is the function
body) 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.
So, my questions:
Why can't I redeclare the variable in the code snippet above?
Supposing I really can't, what I'd really like to do is find
a way to populate variables with the output of functions while
checking the error in a more concise way. So is there any way
to improve on the following form for getting a value out of an
error-able function?
var A RealType
if newA, err := SomeFunc(); err != nil {
return err
} else {
A = newA
}
This is happening because you are using the short variable declaration in an if initialization statement, which introduces a new scope.
Rather than this, where a is a new variable that shadows the existing one:
if a, ok := b.(bool); !ok {
fmt.Println("Problem!")
}
you could do this, where a is redeclared:
a, ok := b.(bool)
if !ok {
fmt.Println("Problem!")
}
This works because ok is a new variable, so you can redeclare a and the passage you quoted is in effect.
Similarly, your second code snippet could be written as:
A, err := SomeFunc()
if err != nil {
return err
}
You did redeclare the variable, that is the problem. When you redeclare a variable, it is a different variable. This is known as "shadowing".
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
// original a.
var a bool
var b interface{}
b = true
// redeclared a here. This is a a'
if a, ok := b.(bool); !ok {
// a' will last until the end of this block
fmt.Println("Problem!")
}
// a' is gone. So using a here would get the original a.
}
As for your second question. That code looks great. I would probably switch it to if err == nil (and swap the if and else blocks). But that is a style thing.
The error raised by the compiler is saying that both declarations of variable a are not being used.
You're actually declaring a twice, when using short declaration in the if condition you're creating a new scope that shadows the previous declaration of the variable.
The actual problem you're facing is that you're never using the variable value which, in Go, is considered a compile time error.
Regarding your second question, I think that the shortest way to get the value and check the error is to do something similar to this:
func main() {
a, ok := someFunction()
if !ok {
fmt.Println("Problem!")
}
}
I just got started with Golang, and I saw the typical swap function example:
func swap(x, y string) (string, string) {
return y, x
}
I automatically thought that the named returns could have solved it and that it was a sweeter example, so I tried the shorter version:
package main
import "fmt"
func swap(z, y int) (z, y int) {
return
}
func main() {
fmt.Println(swap(2, 3))
}
But to by my surprise it didn't compile complaining about a duplicate argument. Why is not possible to return an input argument? Am I doing something wrong or it is just not supported?
I thought this was a totally valid use case and that it could have been many other examples for this usage.
I'm also a Golang beginner. Here's what I managed to find out.
The problem is essentially, that you declare two variables named z, then expect them to be unified. This is not supported, and in fact would go against the main goal of named return types, which is to document the meaning of the values returned.
To explain in more detail, this is a bit like writing the following code:
func badFunction(a int) int {
var a int = 0
return a
}
A variable is declared twice, and this is confusing for Go. If we look at what the 'tour of go' has to say about named return values, we can see the issue. It's not the greatest source, but it's a source nonetheless:
Go's return values may be named. If so, they are treated as variables defined at the top of the function.
That is to say, your example is almost exactly like badFunction. To the compiler, it looks a bit like this:
func swap(a, b int) (int, int) {
var a int = 0
var b int = 0
return b, a
}
Naturally, the compiler complains about a redeclared in block, which is a related though admittedly not equal error. The error message you receive there appears to essentially be a pre-check to prevent the user from seeing the code produced when desugared.
As this Stackoverflow question reports, named return values should essentially be for documentation only. However, it does mention the possibility of accidental shadowing. It may be that an earlier Go version supported this, but has since been changed to prevent bugs due to this kind of name collision, however I have not found anything pertaining to this.
The effective go section on the topic also has something to say:
The return or result "parameters" of a Go function can be given names and used as regular variables, just like the incoming parameters. When named, they are initialized to the zero values for their types when the function begins; if the function executes a return statement with no arguments, the current values of the result parameters are used as the returned values.
The names are not mandatory but they can make code shorter and clearer: they're documentation.
TL;DR: The compiler doesn't unify names in the way you might expect. This kind of implicit shadowing not supported, and should be actively avoided to prevent certain easily avoidable bugs.
I guess problem is not in returning input argument, but in names duplication: y and z are declared twice on the same level and compiler cannot distinguish.
When you declare a variable in return type, Go compiler would consider that, you are declaring the variable there for future use.
Now when the compiler sees the same variable name in both input & return part, it will report a duplicate argument issue.
You can try the working example below, if you want to
func swap(x, y string) (a string, b string) {
a = y
b = x
return
}
You can do this way
func checkError(err *error) (bool, *error) {
if err != nil {
return false, err
} else {
return false, nil
}
}
or if you really want to use variable, this way
func checkError(err *error) (result bool, err_msg *error) {
if err != nil {
return false, err
} else {
return false, nil
}
}
This question already has answers here:
How does defer and named return value work?
(3 answers)
Closed 5 years ago.
I was reading some code written in Golang on Github and found a very interesting piece of code. I simplified it to be clear.
func Insert(docs ...interface{}) (err error) {
for i := 0; i < 3; i++ {
err = fmt.Errorf("")
if err.Error()!="EOF" {
return
}
}
return
}
I'm very confused about empty return here... How it works? Does he return nil as error or breaks for loop? I understand that this question looks dummy, but I cannot find any info on this in go docs... Also, I don't understand how we can return err, which is, as I understood, declared somehow in return. Does (err error) means that we already have an error variable available in our func which is used as default return value if none specified? Why then we implicitly make return err at the end of func?
I'll be very gratefull for explanation.
The function uses a "named" return value.
From the spec on return statements:
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.
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.
Using named returns allows you to save some code on manually allocating local variables, and can sometimes clean up messy if/else statements or long lists of return values.
func a()(x []string, err error){
return
}
is really just shorthand for
func a() ([]string,error){
var x []string
var err error
return x,err
}
Its a bit shorter, and I agree that it may be less obvious.
Named returns are sometimes needed, as it allows things like accessing them inside a deferred function, but the naked return is just syntactic sugar as far as I can tell, and is never strictly required.
One place I see it commonly is in error return cases in functions that have many return values.
if(err != nil){
return
}
return a,b,c,nil
is easier than
if(err != nil){
return nil,nil,nil,err
}
return a,b,c,nil
when you have to write it a bunch of times. And you don't have to modify those returns if you change the signature to have additional "real" return values.
Most places I am using them in the codebase I just searched, they definitely seem to be hiding other smells, like overly complex multi-purpose functions, too deep if/else nesting and stuff like that.
Go's return values may be named. If so, they are treated as variables defined at the top of the function.
package main
import "fmt"
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
fmt.Println(split(17))
}
https://tour.golang.org/basics/7
When you have a named return value (err here):
func Insert(docs ...interface{}) (err error) {
This creates a function-local variable by that name, and if you just call return with no parameters, it returns the local variable. So in this function,
return
Is the same as, and implies,
return err
This is detailed in the tour and in the spec.
In go there are functions which return two values or more values, commonly one is an error. Suppose that I want to store the first return value into an already initialized variable, but I would like to initialize the variable to contain the error inline. Is there a way to do this?
For example, say I had this code
var a int
//This code doesn't compile because err doesn't exist
a, err = SomeFuncWithTwoReturnValues()
//This code doesn't compile either
a, err := SomeFuncWithTwoReturnValues()
I know you could do this, but I was hoping there was a way to do it all inline
var a int
var err error
a, err = SomeFuncWithTwoReturnValues()
or
a, err := SomeFuncWithTwoReturnValues()
EDIT: The code above actually compiles, so I looked back at my code to drill down more and have created a quick sample that actually replicates the problem (not just in my mind...).
package main
func myfunc() (int, int) {
return 1, 1
}
func main() {
a := make([]int, 1)
a[0], b := myfunc()
a[0] = b
}
Compiler says main.go|9| non-name a[0] on left side of :=. If I make it = instead of := though then b is never created. I get the feeling that there is not shorthand way to do it though.
As you've mentioned in the comments, you'll need to use the = operator in order to assign to a variable you've already declared. The := operator is used to simultaneously declare and assign a variable. The two are the same:
var x int
x = 5
//is the same as
x := 5
This solution will at least compile:
package main
func myfunc() (int, int) {
return 1, 1
}
func main() {
var b int
a := make([]int, 1)
a[0], b = myfunc()
a[0] = b
}
To answer your question, I don't think there is a way to simultaneously use an undeclared and a declared variable when returning multiple values. That would be trying to use two different operators simultaneously.
Edit: just saw your example from the code that compiles, so it appears you're already familiar with go's assignment operators. I'll leave the example up anyway.
Golang is not a very consistent language. This is a good example. At the beginning I was confused and it would be much simpler if they would always allow the := operator. The compiler is smart enough to detect already declared variables:
package main
import "fmt"
func testFunc() (int,error) {
return 42,fmt.Errorf("Test Error")
}
func main() {
number1,err := testFunc() // OK
number2,err := testFunc() // OK, even if err is already defined
number1,err = testFunc() // OK
// number1,err := testFunc() // ERROR: no new variables on left side of :=
fmt.Println(number1,number2,err)
}
Playground Link: https://play.golang.org/p/eZVB-kG6RtX
It's not consistent, because golang allows you to use := for already declared variables if you assign to them while also introducing a new variable. So the compiler can detect that variables already exists and skip their declaration. But the golang developers decided to allow that only if you introduce at least one new value. The last example shows that.
I ran into this situation like this:
package main
import "os"
func main() {
var cache struct { dir string }
// undefined: err
cache.dir, err = os.UserCacheDir()
// non-name cache.dir on left side of :=
cache.dir, err := os.UserCacheDir()
if err != nil {
panic(err)
}
println(cache.dir)
}
as you discovered, this issue does not have a clean solution. You can declare
an extra variable:
dir, err := os.UserCacheDir()
if err != nil {
panic(err)
}
cache := userCache{dir}
Or, while more verbose, you can declare the error beforehand. This can save
memory, as Go does not use a Rust ownership model:
var (
cache struct { dir string }
err error
)
cache.dir, err = os.UserCacheDir()
As mention in the spec, while using:=, if one of the variables is new, then the old one will just be assigned with the new data.
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) 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.
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // redeclares offset
As mentioned by the other answers you cannot use assignment and declaration in the same return statement. You have to use either.
However I guess the main reason for your question is cleaning up the code so you don't have to declare an extra err variable above the method or function statement.
You can solve this in two ways:
Declare a global var err error variable and use it in the assignment:
var err error
func MyFunc(someInput string) {
var a int
a, err = someOtherFunction()
}
If your method or function returns an error you can use the declared return variable
func MyFunc(someInput string) (err error) {
var a int
a, err = someOtherFunction()
return
}
I mainly have the problem in methods when I want to assign something to a struct member, e.g.:
type MyStruct struct {
so string
}
func (m *MyStruct) SomeMethod() (err error) {
m.so, err = SomeFunction()
// handle error and continue or return it
return
}
in go tutorial following code is often seen:
a := foo()
b, c := foo()
or actually what I see is:
m["Answer"] = 48
a := m["Answer"]
v, ok := m["Answer"]
how many foo() is defined?
Is it two, one with one return type, another with two return type?
Or just one foo() with two return type defined, and somehow magically when only need one return value (a := foo()), another return value is omitted?
I tried
package main
func main() {
a := foo()
a = 1
}
func foo() (x, y int) {
x = 1
y = 2
return
}
func foo() (y int) {
y = 2
return
}
But I got error message foo redeclared in this block
While some built in operations support both single and multiple return value modes (like reading from a map, type assertions, or using the range keyword in loops), this feature is not available to user defined functions.
If you want two versions of a function with different return values, you will need to give them different names.
The Effective Go tutorial has some good information on this.
Basically, a function defines how many values it returns with it's return statement, and it's function signature.
To ignore one or more of the returned values you should use the Blank Identifier, _(Underscore).
For example:
package main
import "fmt"
func singleReturn() string {
return "String returned"
}
func multiReturn() (string, int) {
return "String and integer returned", 1
}
func main() {
s := singleReturn()
fmt.Println(s)
s, i := multiReturn()
fmt.Println(s, i)
}
Playground
The v, ok := m["answer"] example you've given is an example of the "comma, ok" idiom (Also described in the Effective Go link above). The linked documentation uses type assertions as an example of it's use:
To extract the string we know is in the value, we could write:
str := value.(string)
But if it turns out that the value does not contain a string, the program will crash with a run-time error. To guard against that, use the "comma, ok" idiom to test, safely, whether the value is a string:
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
} else {
fmt.Printf("value is not a string\n")
}
If the type assertion fails, str will still exist and be of type string, but it will have the zero value, an empty string.