Can I get a mixture of := and = in Go if statements? [duplicate] - go

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

defining function inside of function

When defining a inner function which utilizes the variables of outer scope, should I pass the variables to the inner function as parameters?
In my example, generate and generate2 both give me same result, is there a reason I should choose any one of them?
The code picks key 1 to generate combinations with key 3,4,5,
then picks key 2 to generate combinations with key 3,4,5.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
src := map[int][]string{
1: []string{"1", "11", "111"},
2: []string{"2", "22"},
3: []string{"3"},
4: []string{"4"},
5: []string{"5", "55"},
}
result2 := generate2(src)
fmt.Println(result2)
result := generate(src)
fmt.Println(result)
}
func generate(src map[int][]string) []string {
var combo []string
var add = func(f []string) {
for _, v := range f {
for _, p := range src[3] {
for _, q := range src[4] {
for _, r := range src[5] {
combo = append(combo, v+p+q+r)
}
}
}
}
}
add(src[1])
add(src[2])
return combo
}
func generate2(src map[int][]string) []string {
var combo []string
var add = func(f []string, combo []string, src map[int][]string) []string {
for _, v := range f {
for _, p := range src[3] {
for _, q := range src[4] {
for _, r := range src[5] {
combo = append(combo, v+p+q+r)
}
}
}
}
return combo
}
combo = add(src[1], combo, src)
combo = add(src[2], combo, src)
return combo
}
When defining a inner function which utilizes the variables of outer scope, should I pass the variables to the inner function as parameters?
It depends on what you want to achieve.
What you call "a function inside a function" is actually called "a closure" (and some people call it "lambda").
Closures capture variables from the outer lexical scope, referenced in its body. In Go, this capturing is done "by reference" or "by name" which basically means each time a closure is called it will "see" current values of the variables it closes over, not the values these variables had at the time the closure was created—observe that the program:
package main
import (
"fmt"
)
func main() {
i := 42
fn := func() {
fmt.Println(i)
}
fn()
i = 12
fn()
}
would output
42
12
Conversely, when you pass values as arguments to calls to a closure, each call will see exactly the values passed to it.
I hope you now see that what strategy to pick largely depends on what you want.
Conceptually, you may think of a closure as being an instance of an ad-hoc anonymous struct data type, the fields of which are pointers to the variables the closure closes over, and each call to that closure being analogous to calling some (anonymous, sole) method provided by that type (actually, that's what the compiler usually does behind your back to implement a closure).
Such "method" may have arguments, and whether it should have them, and what should go to the type's fields and what should be that method's arguments can be judged using the usual approach you employ with regular types.
In this context, there is no functional difference between the two functions. As you noticed, local functions have access to local variables without explicitly passing them. In your example you might prefer to use generate1 for easier reading.

Redeclare a variable for convenience in golang?

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!")
}
}

Empty return in func with return value in golang [duplicate]

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.

Go: Assign multiple return value function to new and old variable

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
}

how to make function support one or two return value

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.

Resources