Reputation: 5924
I'm playing with Go Playground and find this code:
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
So here I can see that *MyError
implements error
interface. However if I remove &
in error
func and return MyError
instead, I receive compile time error:
prog.go:19: cannot use MyError literal (type MyError) as type error in return argument: MyError does not implement error (Error method has pointer receiver)
. OK I can understand that, so I can make the function Error
like this and this will compile and run successfully:
func (e MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return MyError{
time.Now(),
"it didn't work",
}
}
Then I see in main
func that there is check if err
is nil
so if I understand correctly it is perfectly possible func error
to return nil
in some situations. So it is possible for MyError
struct to take nil
values. But then if I try to compile this:
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return nil
return MyError{
time.Now(),
"it didn't work",
}
}
func main() {
var err2 MyError = nil
fmt.Println(err2)
if err := run(); err != nil {
fmt.Println(err)
}
}
go compiler said that: prog.go:27: cannot use nil as type MyError in assignment
[process exited with non-zero status]
Why in the upper case the compilation is successful and in this case the compilation fails?
Is it possible for structs to be nil
(I guess not, but then why run
func compiles?)
Upvotes: 2
Views: 12708
Reputation: 58409
A nil interface is not the same as an interface containing a nil value.
In your question, you have seen that an interface (in your case, the builtin error interface) can be nil, and drawn an incorrect conclusion that every type that can implement the interface can also be nil.
Upvotes: 3
Reputation: 20355
In the first example, *MyError
implemented the error
interface. As you can see, it's a pointer, and a pointer can have the nil
value.
var err *MyError
err == nil // true
var err *MyError = new(MyError)
err == nil // false
But in the second example, it is MyError
which implements the error
interface, and that is not a pointer any more.
var err MyError
err == MyError{} // true
&err == nil // false
err == nil // Compilation error
It is the adress of err
this time that can be nil
, not the variable itself.
As a comparison, consider the int
type: var i int
. You can check i == 0
for instance, but it would be an error to test if i == nil
, because nil
is not an integer (exactly as it was not a MyError
before). But you could still check if the address of i
is nil
: &i == nil
.
EDIT
Be aware that this function will always return nil
(the execution stops right after the first return
):
func run() error {
return nil
return MyError{
time.Now(),
"it didn't work",
}
}
It compiles because the prototype of the function tells that it must return an error
, and indeed, nil
is a valid error
, and so is a MyError{}
variable. But try to change this function prototype to this one:
func run() MyError
You will see that the compilation fails, because nil
is not a MyError
variable, even though it is a valid value for the error
type.
Upvotes: 11