Why is it possible to redefine the err variable?
err := ipdf.Open(source)
if err != nil {
panic("Couldn't open pdf.")
}
payload, err := ioutil.ReadFile(other)
if err != nil {
panic("Couldn't read other file.")
}
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.
https://golang.org/ref/spec#Short_variable_declarations
I would recommend, as much as possible, using inline checks:
// local scope
if err := ipdf.Open(source); err != nil {
panic("Couldn't open pdf.")
}
payload, err := ioutil.ReadFile(other)
if err != nil {
panic("Couldn't read other file.")
}
Short variable declarations are used mainly when you have to declare variables for temporary use and those variable names can be used in the further program too. For example, "err" can be used anytime in the further program.
Lets assume the language was java, you would had to declare more different variable names for the further program.
But in golang, short variable declarations work as "let" in javascript.
Hope this helps.
Related
Is A same as B?
A
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
rnd.JSON(w, http.StatusProcessing, err)
return
}
B
err := json.NewDecoder(r.Body).Decode(&t);
if err != nil {
rnd.JSON(w, http.StatusProcessing, err)
return
}
They are equivalent except one difference: the scope of the err variable. In the A version the scope of the err variable is the if statement: it's not accessible after the if.
In the B version the err variable will be in scope after the if statement too, and if err is already defined earlier, it could result in a compile-time error too.
It's good practice to always minimize the scope of variables (which gives you less chance to misuse them). If you do not wish to further examine the returned error after the if, it's better to use the A version. If you do need it after the if, then obviously the B version is the way to go.
I declare some variables (offsetI and limitI) outside of a conditional statement. Inside the conditional statement I am trying to assign them values, then use those values for a query after the conditional statement.
var (
number, size, offset, limit string
offsetI, limitI uint64
)
// Get the string values for number, size, offset, and limit
// ...
if size != "" {
// Parse the number value
numberI, err := strconv.ParseUint(number, 10, 64)
if err != nil {...}
// Parse the size value
limitI, err = strconv.ParseUint(size, 10, 64)
if err != nil {...}
// Calculate the offset
offsetI = numberI * limitI
} else {
// Parse the limit value
limitI, err := strconv.ParseUint(limit, 10, 64) // limitI declared and not used
if err != nil {...}
// Parse the offset value
offsetI, err = strconv.ParseUint(offset, 10, 64)
if err != nil {...}
}
// Make the query using offsetI and limitI
result, err := s.GetAllPaginated(offsetI, limitI)
if err != nil {...}
I am not intending to re-declare the limitI variable in the scope of the else statement, but I need to use the := operator for declaring a new err variable.
The only thing I could come up with was to separately declare another err variable, so I could use a regular assignment statement:
} else {
var err error // New
// Regular assignment statement now
limitI, err = strconv.ParseUint(limit, 10, 64)
if err != nil {...}
I would like to be able to do this without having to declare an additional error variable.
The extra var error is awkward, but it's a common way to address this situation. The spec on scoping says (emphasis mine):
The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
So in your case, that short variable declaration is declaring a different limitI scoped to the "innermost containing block." Since it only "lives" until the next closing brace, it isn't used.
In your specific case, an option might be to declare err outside the if/else, since it's used in both inner scopes, so you can use use = instead of := with those functions returning errors. Then there's no "inner limitI" declared and you have no unused variable issue.
"Shadowing" situations like this can also produce unexpected behavior rather than an error. go vet -shadow tries to detect "[v]ariables that may have been unintentionally shadowed" and, different but related, gordonklaus/ineffasign generalizes the "unused variable" check to detect useless assignments even if they weren't declarations.
The only thing I could come up with was to separately declare another err variable
This is being studied in the context of a future Go 2 (2019? 2020).
See "Error Handling — Problem Overview" (Russ Cox - August 27, 2018)
A possible proposed new syntax would avoid having to redeclare err:
Example:
func CopyFile(src, dst string) error {
handle err {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
r := check os.Open(src)
defer r.Close()
w := check os.Create(dst)
handle err {
w.Close()
os.Remove(dst) // (only if a check fails)
}
check io.Copy(w, r)
check w.Close()
return nil
}
Note that go vet -shadow is no longer available since Go 1.12 (February 2019):
The experimental -shadow option is no longer available with go vet.
Checking for variable shadowing may now be done using
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)
As a Go "newb" I'm not sure why I'm receiving the errors undefined err and undefinded user in the console when compiling the program.
I have:
if req.Id == nil {
user, err := signup(C, c, &req)
} else {
user, err := update(C, c, &req)
}
if err != nil {
c.JSON(http.StatusOK, err)
return
}
doSomethingWith(user)
I realise I could probably declare the err and user variables before the conditional block but I would like to know why this doesn't work. Is it something to do with creating two new variables in one go?
UDPATE
Getting in a bit of a mess with this.
I've now got:
user := core.User{}
if req.Id == nil {
user, err := signup(C, c, &req)
} else {
user, err := update(C, c, &req)
}
cleanUser(&user)
and my errors now are user declared and not used. I'm not tackling the err part at the moment but I'm unsure why I'm getting errors about the user.
It's because the scope of the err variable you're creating: it is only in scope (and therefore valid/referable) till the end of innermost block in which you declared it.
Spec: Declarations and scope
The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
When you declare it before the if statement, then it will be in scope till the end of the container block which also includes the 2nd if where you test the err variable, so that is ok.
UDPATE:
Update to your update: you used a Short variable declaration which creates new variables because you used it in a new block. You haven't used these new variables (only the "other" user declared outside the inner block) hence the compile time error "user declared and not used".
Solution is simple: simply declare both variables before the if and don't use short variable declaration but simply assignment:
user := core.User{}
var err error
if req.Id == nil {
user, err = signup(C, c, &req)
} else {
user, err = update(C, c, &req)
}
if err == nil {
cleanUser(&user)
}
Or using one line to declare both user and err:
user, err := core.User{}, error(nil)
I am a newbee to golang, and I write a program to test io package:
func main() {
readers := []io.Reader{
strings.NewReader("from string reader"),
bytes.NewBufferString("from bytes reader"),
}
reader := io.MultiReader(readers...)
data := make([]byte, 1024)
var err error
//var n int
for err != io.EOF {
n, err := reader.Read(data)
fmt.Printf("%s\n", data[:n])
}
os.Exit(0)
}
The compile error is "err declared and not used". But I think I have used err in for statement. Why does the compiler outputs this error?
The err inside the for is shadowing the err outside the for, and it's not being used (the one inside the for). This happens because you are using the short variable declaration (with the := operator) which declares a new err variable that shadows the one declared outside the for.
Is there an advantage to scoping the second err in an if statement after an err has already been created in the scope of foo()? Especially in terms of memory management or being idiomatic.
Version 1
func foo() {
temp, err := something()
if err != nil {
...
}
if err := other(); err != nil {
...
}
}
Version 2
func foo() {
temp, err := something()
if err != nil {
...
}
err = other()
if err != nil {
...
}
}
https://golang.org/doc/effective_go.html#control-structures
Remember, error is an interface. And nil interfaces are zero-length in bytes (and empty structs are zero-length too).
This means there is no additional work for the GC to cleanup either way.
It is a personal preference, and there's even a third way using named return values:
func foo() (err error) {
...
}
Though I highly recommend not using that pattern.
Personally, I prefer the idiomatic nature of inline with the if when I can and really enjoy when the pattern allows me to use it. But remember, the scoping of other variables are only available within the if:
if temp, err := other(); err != nil {
// can only use temp here
...
}
(unless you define the vars ahead of time, which defeats the purpose of inlining anyways)
But most often, I need to keep temp around after the evaluation:
temp, err := something()
if err != nil {
...
}
// continue to use temp
Which means most of my code looks like the above.
But when I run across a pattern that allows it, you bet I'll use it. For example, bufio's Writer.WriteByte:
if err := writer.WriteByte(b); err != nil {
...
}
Where writer and b were defined in the outer scope, most likely with their own err checks. There is zero point of defining err outside the scope of the evaluation.
Limiting the scope of the err value just seems like the idiomatic way, when you can use it.
Advantage of scoping variables:
Since it cannot be accessed from other scope, Data Integrity is preserved.
Only required data can be passed to function , thus protecting the remaining data
see this:
Where can we use Variable Scoping and Shadowing in Go?