gotypesinterfaceassertiongoland

What is the reasoning behind the way Go's does "type assertion"?


I'm trying to wrap my head around the fact that Golang's type assertion only works for the variables that are explicitly defined to be of an interface type and doesn't work for concrete types (i.e. "string", "int32", etc..).

Here's a quick and simple code sample that illustrates the root cause of my confusion:

package main

import "fmt"

// here we define an interface type:
type Shape interface {
    DoubleSize() int32
}

// here we define a new type which is really just an int32:
type Rect int32

// here we make "Rect" type above comply with the "Shape" interface 
// by implementing the methods of that interface,
// and so, since the interfaces in Go are implemented implicitly,
// this should make the "Rect" type an implicit instance of the "Shape" interface
func (theShape Rect) DoubleSize() int32 {
    return int32(theShape) * 2
}

// this function expects its "someShape" parameter to be of "Shape" type)
func whateverFunction(someShape Shape) int32 {
    return someShape.DoubleSize()
}

func main() {
    var newRect = Rect(5)
    // ^^ if this is instead written as "var newRect Shape = Rect(5)" 
    // no error with type assertion happens down the line

    whateverFunction(newRect)
    // ^^ this function works just fine because "newRect" implicitly implements
    // the "Shape" interface — the one that this function expects to receive.  

    // !! but type assertion doesn't work on "newRect"
    // error: invalid operation: newRect (variable of type Rect) is not an interface
    v, ok := newRect.(Shape)
    if !ok {
        fmt.Println("type assertion failed")
        return
    }
    fmt.Println("This is v:", v)
}

As the title of this question suggests, I can't understand the reasoning behind implementing type assertion to only work on interface types, and check if the underlying value assigned to the variable that implements that interface EXPLICITLY is what we specify inside of ".(T)" assertion method. This makes me feel like "type assertion" is an unintentional misnomer that implies that it works for all types but doesn't, only for interface types.

I mean, there obviously must be a reason behind this language design decision, and I think it might have something to do with the way idiomatic Golang is meant to be written, but though I have seen plenty of resources on this matter, they never specify that reason.

The reason that would make some sense to me is if the Go program should, "preferably (I assume, since explicit interface definition is optional)", be written with all variables representing some interface (behavior), and therefore defining an explicit interface on a variable makes sense for the purposes of clarity and readability.

But as I mentioned, I have never actually seen any resources specify why this is how the "type assertion" functionality is implemented in Go, and I am hoping you could help me clarify this confusion.

-- upd 1 - Adding a bit to clarify my question:

At it's core, I think, my question is about the reason (that I do not understand) type assertion only works when interface of the variable is implemented explicitly but not when the the interface is implemented implicitly.

As demonstrated by the "whateverFunction", the code does consider "newRect" to implement the "Shape" interface, or "be the implementation of the "Shape" interface" (otherwise the function wouldn't work with that variable, but it does), but the code behind type assertion ".(T)" method doesn't consider "newRect" to be the implementation of the "Shape" interface.

Therefore, if there is a differentiation in Golang on what to consider an implementation of the interface, I figured there must then be a reason behind such a design decision (to differentiate).

And that is why I mentioned that the only reason so far that I could think of is if this was a way to make people write Go code in a certain way.


Solution

  • You can check out Burak Serdar's answer - you might find it more concise and helpful. Nevertheless, I will post the entire chain of reasoning that made it finally *click* for me:

    |-> Interfaces are used when we cannot be certain of the exact type of the data that we are expecting to receive (because, for example, as a result of user's input, the same parameter in some one function in a program might receive data of different types), but we know the exact behavior that the provided data should have.

    ^^ So it follows that the actual type of the value that will be stored in an interface is unknown at compile time. (Otherwise, obviously, we would've just specified it right there in the code.)

    | -> Therefore we are given type assertion to be able to define program's behavior based on a possible values that we expect a program will be provided with during its execution.

    |-> Therefore the reason why type assertion works only on variables explicitly specified to be of an interface type and not on those that might implement the same interface but are not explicitly specified to be of an interface type

    is because

    we only ever need such type assertion during runtime, when we use interfaces because we don't know the exact type of data that will be sent to the program - it's a necessity that comes up only when using the interfaces therefore type assertion only works for types explicitly specified to be interface types, because in all other cases the type of data is known (allowing compiler to assume the implementation of an interface by a variable implicitely - because it already knows all the data types of the data involved) and we simply never need to use type assertion with the data the type of which is already known.