This question already has answers here:
Hiding nil values, understanding why Go fails here
(3 answers)
Closed 7 years ago.
I have a situation where the value of a variable "err error" value can only be "nil" but asserts "( err == nil ) == false" once it has been reassigned.
Example code below:
package main
import (
"fmt"
"log"
)
type TestError struct {
Message string
}
func (e *TestError) Error() string {
return e.Message
}
func NewTestError(err error) *TestError {
if err == nil {
return nil
}
log.Printf("NewTestError( ... ): creating new NewTestError err = %s", err)
return &TestError{Message: err.Error()}
}
func main() {
var err error
_, err = fmt.Printf("excuse.\n")
err = NewTestError(err)
log.Printf("main( ... ): err == nil. %v", (err == nil))
log.Printf("main( ... ): err = %#v", err)
}
I get the following output from the above code:
excuse.
2015/07/30 08:28:28 main( ... ): err == nil. false
2015/07/30 08:28:28 main( ... ): err = (*main.TestError)(nil)
How can those last two lines be output?
For an interface value (like an error value) to compare equal to nil, both the actual contained value and the actual contained type have to be nil. In your case you have an interface value, which holds a nil pointer, but a *TestError (i.e. non-nil) type.
PS. Just in case it's not clear what are these "interface values", you can check this http://research.swtch.com/interfaces
In the second case you're printing the interface instances information which is the type and the value, type being (*main.TestError) value being (nil). In the first case what you're actually comparing isn't nil because it's also the interface.
Related
I'm in a situation where all my variables are stored as strings. E.g. var boolVar = "false". Is there some package in Go for returning the datatype of the value in boolVar?
I can think of a bit cumbersome way of using strconv and then testing the returned error using if-else, but I wonder if some package already provides this functionality.
var boolVar = "false"
var type = "string"
if _, err := strconv.ParseBool(boolVar); err == nil {
type = "bool"
}
There isn't any silver-bullet solution for your problem, you should check all types one by one.
Firstly go is not built for these requirements,
but there is a package for you requirement which does this: go eval
remember it can eval expr only and not code like eval in python can.
package main
import (
"fmt"
"github.com/apaxa-go/eval"
)
func main() {
str := "int64(10*(1.1+2))"
exp, err := eval.ParseString(str, "")
if err != nil {
fmt.Println(err)
}
val, err := exp.EvalToInterface(nil)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%v %T", val, val)
}
This question already has answers here:
How to compare Go errors
(9 answers)
Closed 7 months ago.
I create a err in my package and compare it with io.EOF, but the == operand is false. But their type and value are same? Why == operand return false?
func TestErr(t *testing.T) {
err := errors.New("EOF")
t.Log(err == io.EOF)
t.Logf("io err:%T,%v,%p", io.EOF, io.EOF, io.EOF)
t.Logf("my err:%T,%v,%p", err, err, err)
}
These two error are not equal because their pointers are not equal
error is an interface. It contains a pointer to the underlying value. The io.EOF is created by:
var EOF = errors.New("EOF")
If you look at errors.New:
func New(text string) error {
return &errorString{text}
}
type errorString struct {
s string
}
So, the io.EOF points to an instance of errorString struct, and the error you created also points to an instance of errorString struct with the same string value, but the two pointers are not the same.
If you really want to do this, you can unwrap the errors:
package main
import (
"errors"
"io"
)
func main() {
err := errors.New("EOF")
println(err.Error() == io.EOF.Error())
}
https://golang.org/pkg/builtin#error
This question already has answers here:
Hiding nil values, understanding why Go fails here
(3 answers)
Closed 5 years ago.
Can someone provide some explanation about this code behaviour:
https://play.golang.org/p/_TjQhthHl3
package main
import (
"fmt"
)
type MyError struct{}
func (e *MyError) Error() string {
return "some error"
}
func main() {
var err error
if err == nil {
fmt.Println("[OK] err is nil ...")
}else{
fmt.Println("[KO] err is NOT nil...")
}
isNil(err)
var err2 *MyError
if err2 == nil {
fmt.Println("[OK] err2 is nil ...")
}else{
fmt.Println("[KO] err2 is NOT nil...")
}
isNil(err2)
}
func isNil(err error){
if err == nil {
fmt.Println("[OK] ... still nil")
}else{
fmt.Println("[KO] .. why not nil?")
}
}
Output is:
[OK] err is nil ...
[OK] ... still nil
[OK] err2 is nil ...
[KO] .. why err2 not nil?
I found this post Check for nil and nil interface in Go but I still don't get it...
error is a built-in interface, and *MyError implements that interface. Even though the value of err2 is nil, when you pass it to isNil, the function gets a non-nil interface value. That value contains information about the type (*MyError) and the value itself, which is a nil pointer.
If you try printing err in isNil, you'll see that in the second case, you get "some error" even though err2 is nil. This demonstrates why err is not nil in that case (it has to contain the type information).
From my understanding your var err2 *MyError definition is generating a pointer to the struct definition and NOT an actual instantiated object.
In Go interface types are represented by a structure with 2 fields: one denotes its actual type and the other is a pointer to the value.
It means that an interface value is never equal to nil unless the value you passed is actually a nil value.
Relevant and good talk: GopherCon 2016: Francesc Campoy - Understanding nil
How would you write a test for a function that returns a value that could either be nil or a concrete value? I don't care about the actual value itself, I just care about whether the value has been returned or not.
type CustomType struct{}
func TestSomeFunc(t *testing.T) {
case := map[string]struct {
Input string
Expected *CustomType // expected result
Error error // expected error value
} {
"test case 1":
"input",
&CustomType{}, // could be nil or a concrete value
nil,
"test case 2":
"input",
nil, // could be nil or a concrete value
ErrSomeError,
}
actual, err := SomeFunc(case.Input)
if (actual != case.Expected) {
t.Fatalf(...)
}
}
and the function to be tested could look something like:
func SomeFunc(input string) (*CustomType, error) {
foo, err := doSomething()
if err != nil {
return nil, err
}
return foo, nil
}
I guess the logic I want is:
if ((case.Expected != nil && actual == nil) ||
(case.Expected == nil && actual != nil)) {
t.Fatalf(...)
}
Is there a better way of just asserting for existence rather than comparing concrete types?
It's not much shorter than what you have, but I think what you want is for the test to pass only when (case.Expected == nil) == (actual == nil), comparing the (true or false) results of two comparisons against nil. Here's a short program demonstrating:
package main
import (
"fmt"
)
func main() {
isNil, isNotNil, otherNotNil := []byte(nil), []byte{0}, []byte{1}
fmt.Println("two different non-nil values\t", (otherNotNil == nil) == (isNotNil == nil))
fmt.Println("a nil and a non-nil value\t", (isNil == nil) == (isNotNil == nil))
fmt.Println("two nil values\t\t\t", (isNil == nil) == (isNil == nil))
}
As user icza points out, you can change the outer == into a != (giving you something like (actual == nil) != (expected == nil)) to get true when there is a mismatch rather than when there is a match.
Here's an issue that's bedeviling me at the moment. When getting input from the user, I want to employ a loop to ask the user to retry until they enter valid input:
// user_input.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Please enter an integer: ")
var userI int
for {
_, err := fmt.Scanf("%d", &userI)
if err == nil {
break
}
fmt.Println("Sorry, invalid input. Please enter an integer: ")
}
fmt.Println(userI)
}
Running the above, if the user enters valid input, no problem:
Please enter an integer:
3
3
exit code 0, process exited normally.
But try inputting a string instead?
Please enter an integer:
what?
Sorry, invalid input. Please enter an integer:
Sorry, invalid input. Please enter an integer:
Sorry...
Etc, and it keeps looping character by character until the string is exhausted.
Even inputting a single character loops twice, I assume as it parses the newline.
Anyways, there must be a way to flush Stdin in Go?
P.S. In the absence of such a feature, how would you work around it to provide equivalent functionality? I've failed even at that...
I would fix this by reading until the end of the line after each failure. This clears the rest of the text.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
fmt.Println("Please enter an integer: ")
var userI int
for {
_, err := fmt.Fscan(stdin, &userI)
if err == nil {
break
}
stdin.ReadString('\n')
fmt.Println("Sorry, invalid input. Please enter an integer: ")
}
fmt.Println(userI)
}
Is it bad to wake up an old question?
I prefer to use fmt.Scanln because A) it doesn't require importing another library (e.g. reader) and B) it doesn't involve an explicit for loop.
func someFunc() {
fmt.Printf("Please enter an integer: ")
// Read in an integer
var i int
_, err := fmt.Scanln(&i)
if err != nil {
fmt.Printf("Error: %s", err.Error())
// If int read fails, read as string and forget
var discard string
fmt.Scanln(&discard)
return
}
fmt.Printf("Input contained %d", i)
}
However, it seems like there ought to be a more elegant solution. Particularly in the case of fmt.Scanln it seems odd that the read stops after the first non-number byte rather than "scanning the line".
I ran into a similar problem for getting user input but solved it in a slightly different way. Adding to the thread in case someone else finds this useful:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// Get first word from stdin
func getFirstWord() (string) {
input := bufio.NewScanner(os.Stdin)
input.Scan()
ans := strings.Fields(input.Text())
if len(ans) == 0 {
return ""
} else {
return ans[0]
}
}
func main() {
fmt.Printf("Would you like to play a game?\n> ")
ans := getFirstWord()
fmt.Printf("Your answer: %s\n", ans)
}
I know this has already been answered but this was my implementation:
func flush (reader *bufio.Reader) {
var i int
for i = 0; i < reader.Buffered(); i++ {
reader.ReadByte()
}
}
This should work in every situation, including ones where "stdin.ReadString('\n')" cannot be used.
Sorry for digging this back up, but I ran into this today and wanted to improve on the existing answers by using new standard library functionality.
import (
"bufio"
"fmt"
"os"
)
func discardBuffer(r *bufio.Reader) {
r.Discard(r.Buffered())
}
stdin := bufio.NewReader(os.Stdin)
var i int
for true {
if _, err := fmt.Fscanln(stdin, &i); err != nil {
discardBuffer(stdin)
// Handle error, display message, etc.
continue
}
// Do your other value checks and validations
break
}
The basic idea is to always buffer your reads from stdin. When you encounter an error while scanning, just discard the buffer contents. That way you start with an empty buffer for your next scan.
Alternatively, you can discard the buffer before you scan, so any stray inputs by the user before then won't get picked up.
func fscanln(r *bufio.Reader, a ...interface{}) error {
r.Discard(r.Buffered())
_, err := fmt.Fscanln(r, a...)
return err
}
stdin := bufio.NewReader(os.Stdin)
var i int
if err := fscanln(stdin, &i); err != nil {
// Handle error
}
I use this snippet to filter unnecessary leading space/new line
in := bufio.NewReader(os.Stdin)
result, err = in.ReadString('\n')
for len(strings.TrimSpace(result)) == 0 {
result, err = in.ReadString('\n')
}
I usually use bufio.Scanner since the fmt.Scan funcs always split on whitespace.
func promptYN(msg string) bool {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", msg)
s.Scan()
input := strings.ToLower(s.Text())
if input == "y" || input == "n" {
return input == "y"
}
fmt.Println("Error: expected Y or N.")
}
}
func promptInt(msg string) int {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s [int]: ", msg)
s.Scan()
output, err := strconv.Atoi(s.Text())
if err == nil {
return output
}
fmt.Println("Error: expected an integer.")
}
}
Or you could make something more universal:
func prompt(msg string, check func(string) bool) {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s: ", msg)
s.Scan()
if check(s.Text()) {
return
}
}
}
Example:
var f float64
prompt("Enter a float", func(s string) bool {
f, err = strconv.ParseFloat(s, 64)
return err == nil
})