package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)
func main() {
sess := session.Must(session.NewSession())
client := route53.New(sess)
zones, err := client.ListHostedZones(&route53.ListHostedZonesInput{
MaxItems: aws.String("100"),
})
if err != nil {
fmt.Printf("Error occurred")
}
fmt.Printf("%v", zones)
if zones.IsTruncated {
fmt.Printf("%v", zones.NextMarker)
}
}
The code above fails due to the following condition.
if zones.IsTruncated {
fmt.Printf("%v", zones.NextMarker)
}
with the result of
non-bool zones.IsTruncated (type *bool) used as if condition
What I want to know is why is there a difference between a regular type(bool) and type(*bool). I understand one is a pointer value however, the condition should still be useful.
Here is a tailed snippet of the output without the condition in place
IsTruncated: true,
MaxItems: "100",
NextMarker: "Wouldn'tYouLikeToKnow"
}
From standard reference:
"If" statements specify the conditional execution of two branches according to the value of a boolean expression.
So, to answer your question, if statements explicitly require boolean expressions. *bool is still not bool. You need to explicitly dereference the pointer (which could even be nil in which case your program will panic).
Here's how you would re-write your if statement:
if *zones.IsTruncated {
fmt.Printf("%v", zones.NextMarker)
}
but make sure it is not nil
You need to dereference to a struct. You need to use the := operator to do so. Something like -
new_struct := *your_pointer
You would then use the new_struct instead.
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 am new to Go.
Currently, I am creating a menu in Go and I want to verify that the data type of the input from the user matches the data type of the variable defined in the code. Part of my code looks like this so far:
package main
import (
"fmt"
"reflect"
)
var option int // The variable is declared outside of the main().
func general_menu() {
fmt.Println(".......................General Menu..................................")
fmt.Println()
fmt.Println("Calculator..........................................................1")
fmt.Println("Linear algebra package..............................................2")
fmt.Println("Language change.....................................................9")
fmt.Println("Exit...............................................................10")
fmt.Println()
fmt.Println("Choose an option from the menu.")
fmt.Println()
fmt.Scan(&option)
fmt.Println()
if (option != 1 && option != 2 && option != 9 && option != 10)||reflect.TypeOf(option)!=int{
fmt.Println("Wrong option input. Please, try again.")
fmt.Println()
general_menu()
}
}
I know that this doens't work this way, and I know that "int" can not be used as part of an "if" condirion.
I would kindly appreciate any suggestions on the proper way to solve this problem.
Thanks.
Edit: I have added more of my code as kindly suggested by the contributors.
Edit: Based on the answer provided, I have tried to implement a function, but the syntax is still not correct:
func check_integers_are_not_string(x int) bool {
change := strconv.Itoa(x)
if change != nil {
return true
} else {
return false
}
} // This function returns a true boolean value if conversion from int to string was possible, meaning that the entered value is a string.
Just read the documentation of Scan - https://pkg.go.dev/fmt#Scan
It returns the number of successfully read arguments and an error. The input is mapped in your case to a variable of type int, so if a user inputs a string it will return 0 and an error. Otherwise it will return 1 and the error should be nil. You can check for that.
n, err := fmt.Scan(&option)
if n != 1 || err != nil {
// print error and go back
}
One common way to do it is to try to make the conversion and see if it succeeds.
optionInt, err := strconv.Atoi(option) // Assuming option is of type string
if err != nil {
log.Printf("String '%s' cannot be converted to type int: %v", option, err)
os.Exit(1)
}
log.Printf(`optionInt is %d.`, optionInt)
This is a good approach if you are only interested in conversion to one type. Otherwise things can quickly get more involved, utilizing constructs such as lexers and parsers, but that would warrant more information on what you are trying to accomplish.
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'm new to golang; however based on my current knowledge I understand that a value-type and a reference-type can both fulfill an interface. But it seems in regards to type assertion, how you return a struct does matter. See the following:
package main
import (
"fmt"
)
type SomeError interface {
Error() string
}
type ConcreteError struct{}
func (ConcreteError) Error() string {
return "?"
}
func returnPointer() SomeError {
return &ConcreteError{}
}
func returnVal() SomeError {
return ConcreteError{}
}
func main() {
pointer := returnPointer()
value := returnVal()
_, pointerWithPointer := pointer.(*ConcreteError);
_, pointerWithValue := pointer.(ConcreteError);
_, valueWithValue := value.(ConcreteError);
_, valueWithPointer := value.(*ConcreteError)
fmt.Printf("Returning a pointer, assert using (*ConcreteError): %v\n", pointerWithPointer); // true
fmt.Printf("Returning a pointer, assert using (ConcreteError): %v\n", pointerWithValue); // false
fmt.Printf("Returning a value, assert using (ConcreteError): %v\n", valueWithValue); // true
fmt.Printf("Returning a value, assert using (*ConcreteError): %v\n", valueWithPointer); // false
}
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
link to play: here
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
It depends. If you need the type assertion to pass - you surely need to know the exact type of the value. a and *a are different types.
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
No.
I have an error value which when printed on console gives me Token is expired
How can I compare it with a specific error value? I tried this but it did not work:
if err == errors.New("Token is expired") {
log.Printf("Unauthorised: %s\n", err)
}
Declaring an error, and comparing it with '==' (as in err == myPkg.ErrTokenExpired) is no longer the best practice with Go 1.13 (Q3 2019)
The release notes mentions:
Go 1.13 contains support for error wrapping, as first proposed in the Error Values proposal and discussed on the associated issue.
An error e can wrap another error w by providing an Unwrap method that returns w.
Both e and w are available to programs, allowing e to provide additional context to w or to reinterpret it while still allowing programs to make decisions based on w.
To support wrapping, fmt.Errorf now has a %w verb for creating wrapped errors, and three new functions in the errors package ( errors.Unwrap, errors.Is and errors.As) simplify unwrapping and inspecting wrapped errors.
So the Error Value FAQ explains:
You need to be prepared that errors you get may be wrapped.
If you currently compare errors using ==, use errors.Is instead.
Example:
if err == io.ErrUnexpectedEOF
becomes
if errors.Is(err, io.ErrUnexpectedEOF)
Checks of the form if err != nil need not be changed.
Comparisons to io.EOF need not be changed, because io.EOF should never be wrapped.
If you check for an error type using a type assertion or type switch, use errors.As instead. Example:
if e, ok := err.(*os.PathError); ok
becomes
var e *os.PathError
if errors.As(err, &e)
Also use this pattern to check whether an error implements an interface. (This is one of those rare cases when a pointer to an interface is appropriate.)
Rewrite a type switch as a sequence of if-elses.
This answer is for Go 1.12 and earlier releases.
Define an error value in a library
package fruits
var NoMorePumpkins = errors.New("No more pumpkins")
Do not create errors with errors.New anywhere in the code but return the predefined value whenever error occurs and then you can do the following:
package shop
if err == fruits.NoMorePumpkins {
...
}
See io package errors for reference.
This can be improved by adding methods to hide the check implementation and make the client code more immune to changes in fruits package.
package fruits
func IsNoMorePumpkins(err error) bool {
return err == NoMorePumpkins
}
See os package errors for reference.
Try
err.Error() == "Token is expired"
Or create your own error by implementing the error interface.
It's idiomatic for packages to export error variables that they use so others can compare against them.
E.g. If an error would came from a package named myPkg and was defined as:
var ErrTokenExpired error = errors.New("Token is expired")
You could compare the errors directly as:
if err == myPkg.ErrTokenExpired {
log.Printf("Unauthorised: %s\n", err)
}
If the errors come from a third party package and that doesn't use exported error variables then what you can do is simply to compare against the string you get from err.Error() but be careful with this approach as changing an Error string might not be released in a major version and would break your business logic.
The error type is an interface type. An error variable represents any value that can describe itself as a string. Here is the interface's declaration:
type error interface {
Error() string
}
The most commonly-used error implementation is the errors package's unexported errorString type:
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
See this working code output (The Go Playground):
package main
import (
"errors"
"fmt"
"io"
)
func main() {
err1 := fmt.Errorf("Error")
err2 := errors.New("Error")
err3 := io.EOF
fmt.Println(err1) //Error
fmt.Printf("%#v\n", err1) // &errors.errorString{s:"Error"}
fmt.Printf("%#v\n", err2) // &errors.errorString{s:"Error"}
fmt.Printf("%#v\n", err3) // &errors.errorString{s:"EOF"}
}
output:
Error
&errors.errorString{s:"Error"}
&errors.errorString{s:"Error"}
&errors.errorString{s:"EOF"}
Also see: Comparison operators
Comparison operators compare two operands and yield an untyped boolean
value. In any comparison, the first operand must be assignable to the
type of the second operand, or vice versa.
The equality operators == and != apply to operands that are
comparable.
Pointer values are comparable. Two pointer values are equal if they
point to the same variable or if both have value nil. Pointers to
distinct zero-size variables may or may not be equal.
Interface values are comparable. Two interface values are equal if
they have identical dynamic types and equal dynamic values or if both
have value nil.
A value x of non-interface type X and a value t of interface type T
are comparable when values of type X are comparable and X implements
T. They are equal if t's dynamic type is identical to X and t's
dynamic value is equal to x.
Struct values are comparable if all their fields are comparable. Two
struct values are equal if their corresponding non-blank fields are
equal.
So:
1- You may use Error(), like this working code (The Go Playground):
package main
import (
"errors"
"fmt"
)
func main() {
err1 := errors.New("Token is expired")
err2 := errors.New("Token is expired")
if err1.Error() == err2.Error() {
fmt.Println(err1.Error() == err2.Error()) // true
}
}
output:
true
2- Also you may compare it with nil, like this working code (The Go Playground):
package main
import (
"errors"
"fmt"
)
func main() {
err1 := errors.New("Token is expired")
err2 := errors.New("Token is expired")
if err1 != nil {
fmt.Println(err1 == err2) // false
}
}
output:
false
3- Also you may compare it with exact same error, like this working code
(The Go Playground):
package main
import (
"fmt"
"io"
)
func main() {
err1 := io.EOF
if err1 == io.EOF {
fmt.Println("err1 is : ", err1)
}
}
output:
err1 is : EOF
ref: https://blog.golang.org/error-handling-and-go
It's being discouraged to compare errors by strings. Instead you should compare errors by value.
package main
import "errors"
var NotFound = errors.New("not found")
func main() {
if err := doSomething(); errors.Is(err, NotFound) {
println(err)
}
}
func doSomething() error {
return NotFound
}
It is especially useful if you are library author and would like to export errors so users can act differently on different type of errors. Standard library does it as well.
Problem with this approach is that exported values can be changed by anyone as Go doesn't support immutable values. Nothing prevents you, though, to use string as an error and make it const.
package main
type CustomError string
func (ce CustomError) Error() string {
return string(ce)
}
const NotFound CustomError = "not found"
func main() {
if err := doSomething(); errors.Is(err, NotFound) {
println(err)
}
}
func doSomething() error {
return NotFound
}
It is more verbose but safer approach.
You should first consider comparing errors by value, as described in other solutions with:
if errors.Is(err1, err2) {
// do sth
}
However in some cases the error returned from a function is a bit complex, e.g. an error is being wrapped multiple times, with a context being added to it in each function call like fmt.Errorf("some context: %w", err), and you may simply just want to compare the error message of two errors. In such cases you can do this:
// SameErrorMessage checks whether two errors have the same messages.
func SameErrorMessage(err, target error) bool {
if target == nil || err == nil {
return err == target
}
return err.Error() == target.Error()
}
func main() {
...
if SameErrorMessage(err1, err2) {
// do sth
}
}
Note that if you simply use
if err1.Error() == err2.Error() {
// do sth
}
You might face nil pointer dereference runtime error if either of err1 or err2 be nil.
To add to #wst 's answer, in some cases, the errors.Is(err, NotFound) approach may not work for reasons I am trying to figure out too. If someone knows, please let me know in the comments.
But I found a better approach to use it in the following way which was working for me:
if NotFound.Is(err) {
// do something
}
Where var NotFound = errors.New("not found") is an exported common error declared.
In my case, the solution was
if models.GetUnAuthenticatedError().Is(err) {
// Do something
}
I want to post one case where errors.Is could work well for custom errors with non-comparable values.
type CustomError struct {
Meta map[string]interface{}
Message string
}
func (c CustomError) Error() string {
return c.Message
}
var (
ErrorA = CustomError{Message: "msg", Meta: map[string]interface{}{"key": "value"}}
)
func DoSomething() error {
return ErrorA
}
func main() {
err := DoSomething()
if errors.Is(err, ErrorA) {
fmt.Println("error is errorA")
} else {
fmt.Println("error is NOT errorA")
}
}
Output
error is NOT errorA
Playground
The reason is errors.Is checks whether the target is comparable or not
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
The comparable type in Go are
booleans, numbers, strings, pointers, channels, arrays of comparable types, structs whose fields are all comparable types
Since the Meta map[string]interface{} of CustomError is NOT comparable, so errors.Is checks failed.
One workaround is declare the ErrorA = &CustomError{Message: "msg", Meta: map[string]interface{}{"key": "value"}} as pointer.