I can convert an int to a float64 like this:
var a int = 10
var b float64 = float64(a)
With regards to type assertions, Effective Go states: 'The type must either be the concrete type held by the interface, or a second interface type that the value can be converted to.'
With that in mind, why does the following fail:
func foo(a interface{}) {
fmt.Println(a.(float64))
}
func main() {
var a int = 10
foo(a)
}
This causes a panic: interface conversion: interface is int, not float64
.
Note that the Go Spec says:
'For an expression x of interface type and a type T, the primary expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T.'
Which does contradict the Effective Go statement but seems more in line with what I see.
Type conversion happens when we assign the value of one data type to another. Statically typed languages like C/C++, Java, provide the support for Implicit Type Conversion but Golang is different, as it doesn't support the Automatic Type Conversion or Implicit Type Conversion even if the data types are compatible.
A type assertion brings out the concrete type underlying the interface, while type conversions change the way you can use a variable between two concrete types that have the same data structure.
A type assertion provides access to an interface value's underlying concrete value. This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t . If i does not hold a T , the statement will trigger a panic.
interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface ( interface{} is an empty interface). In your example, Msg field can have value of any type.
This sentence in Effective Go seems indeed to be confusing. It looks like the author was thinking about structs at that time.
The chapter on assertions in the specification is much clearer :
For an expression x of interface type and a type T, the primary expression
x.(T) asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.
More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.
The fact you can convert your int to a float (and the reverse) doesn't at all mean you can assert they're the same type.
The type must either be the concrete type held by the interface, or a second interface type that the value can be converted to
This basically explains the following:
package main
import "fmt"
type Stringer interface {
String()
}
type Byter interface {
Bytes()
}
type Stringbyter interface {
Stringer
Byter
}
type Polymorphic float64
func (p *Polymorphic) String() {}
func (p *Polymorphic) Bytes() {}
func main() {
i := interface{}(new(Polymorphic))
if _, ok := i.(Stringer); ok {
fmt.Println("i can be asserted to Stringer")
}
if _, ok := i.(Byter); ok {
fmt.Println("i can be asserted to Byter")
}
if _, ok := i.(Stringbyter); ok {
fmt.Println("i can be asserted to Stringbyter")
}
if _, ok := i.(*Polymorphic); ok {
fmt.Println("i can be asserted to *Polymorphic")
}
if _, ok := i.(int); ok {
fmt.Println("i can be asserted to int") // Never runs
}
}
The assertion to int
fails because it's a concrete type (as opposed to interface type) which is not *Polymorphic
itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With