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.
Related
Go 1.20 introduces the errors.Join function that can wrap multiple errors. Are there any issues with calling this function and only passing in a single error?
For example, this article recommends against using the defer f.Close() idiom for writable files, because that would silently ignore any error returned by Close. Instead, it suggests using a named return value err to allow the return value of Close to be be propagated - unless doing so would overwrite an earlier error:
defer func() {
cerr := f.Close()
if err == nil {
err = cerr
}
}()
It seems more correct to use errors.Join in this scenario:
defer func() {
cerr := f.Close()
err = errors.Join(err, cerr)
}()
If both err and cerr are non-nil, this will now return both errors. If both are nil, it will return nil.
However, if one is nil and the other non-nil, errors.Join will not just return the non-nil error but an errors.joinError wrapper around it. Could wrapping an error like this cause any problems? Especially if several functions in the call stack use this approach, so a single error could end up within multiple layers of wrapper?
If errors.JoinError has only one non-nil error, that is still a join-error, and errors.As and errors.Is functions work as expected. This is correct no matter the level of nesting of joined errors.
The only potential problem would be if there is code like:
err:=someFunc()
if err==io.EOF {
...
}
then this will fail. This code has to be rewritten to use errors.Is.
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)
If I do
s, err := os.Stat(path)
and err != nil I need to know if the file doesn't exist vs I don't have permission to access it, etc. How do I get the underlying error code? Reading the os package docs it seems to suggest that I read the text of the error string - surely not?
What FUZxxl says.
From the os.Stat documentation:
Stat returns a FileInfo describing the named file. If there is an error, it will be of type *PathError.
PathError is documented on the same page, stating that it holds the operation that caused the error, the path to the file that caused it and the underlying system's error. In case the file was not found when calling os.Stat, the returned error would be something like this:
&PathError{"stat", "/your/file", syscall.Errno(2)}
Since the underlying error is inherently depending on the OS you use, the only thing that you can do is to
understand PathError.Err. For UNIX systems the syscall package has the Errno error type returned by syscalls like syscall.Stat. You can compare this value with the constants in the syscall package and handle the error (Click to play):
stat, err := os.Stat(file)
if perr, ok := err.(*os.PathError); ok {
switch perr.Err.(syscall.Errno) {
case syscall.ENOENT: fmt.Println("No such file or directory.")
default: panic("Unknown error")
}
}
The shorter way of doing this is to use os.IsNotExist which does pretty much the above
and is, most importantly, platform independent:
stat, err := os.Stat(file)
if err != nil && os.IsNotExist(err) {
// ...
}
The other answer is great, but I wanted add a note about this suggestion:
stat, err := os.Stat(file)
if err != nil && os.IsNotExist(err) {
// ...
}
I found that in many cases, I was needing to take different actions depending on
each test, so in reality you have three branches here. Here is code I use for
that:
stat, err := os.Stat(file)
if os.IsNotExist(err) {
// branch one
} else if err != nil {
// branch two
}
// branch three
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?