Go's interface
is tricky. It takes some experimenting with it to get a good feel for how to use it. That's why the tutorial you're working with has you write things using it: you have to actually use it for a while before you can get to the point where you understand how to use it. :-)
It's worth correcting some nits here though:
type ErrNegativeSqrt float64 //This is the custom Struct
This is not a struct
type. float64
itself is a predeclared numeric type, as defined here in the Go reference:
A numeric type represents sets of integer or floating-point values. The predeclared architecture-independent numeric types are: [various entries snipped out here]
float64 the set of all IEEE-754 64-bit floating-point numbers
Your type
declaration here creates a new type named ErrNegativeSqrt
that has float64
as its underlying type; see the Types section.
A type—any type, whether struct
or not—either implements an interface type, or does not. An interface type is, well, complicated; see the Interface types section for the full picture. Go's error
is a predeclared interface type, as defined by the Errors section:
The predeclared type error
is defined as
type error interface {
Error() string
}
This means that whenever you define your own type UserT, if you add a line like this:
func (varname UserT) Error() string {
// return some string here
}
your defined type UserT now implements the error interface. As such, you can convert any value of type UserT to error
via simple assignment. See the Assignability section, which includes:
A value x
is assignable to a variable of type T
... if one of the following conditions applies: [some bullet items snipped]
T
is an interface type and x
implements T
We just saw that error
is an interface type. So, if some situation calls for a variable of type error
, and you have some existing value x
of type UserT
where UserT
has a method Error() string
, you can assign that value x
into the variable of type error
. That is, given ErrNegativeSqrt
set up as you did, and:
var x ErrNegativeSqrt
// set `x` to some valid value
// and:
var e error
then:
e = x
is valid and assigns x
into e
, though the type of e
itself is an interface type, rather than ErrNegativeSqrt
.
When you stuff a copy of the value of x
into e
like this, the compiler actually sets up two things inside e
. In other words, e
acts a lot like a two-element array, or a struct
with two fields, except that you can't access the two fields with subscripts or .field
type spellings.
One of these two values—the first half of e
's value—is x
's concrete type. The other of these two values is x
's actual value, copied into the other half of e
.
In other words, the compiler turns:
e = x
into the rough equivalent of:
e.value = x
e.type = ErrNegativeSqrt
except, again, for the fact that you can't actually write e.type
and e.value
. The compiler does the two-part-izing for you, automatically.
You should have already seen how you can pick apart the two parts of things stored into e
if necessary. It's often not necessary, especially if the interface is well designed. But it's worth backtracking to these earlier sections of the Tour after experimenting with error returns in #20.