Why does golang prohibit assignment to same underlying type when one is a native type?

前端 未结 2 1167
长发绾君心
长发绾君心 2021-01-22 10:55

Consider this code:

package main
import \"fmt\"

type specialString string

func printString(s string) {
    fmt.Println(s)
}

// unlike, say, C++, this is not l         


        
相关标签:
2条回答
  • 2021-01-22 11:42

    This is because Go does not have class inheritance. It uses struct composition instead. Named types do not inherit properties from their underlying type (that's why it's not called "base type").

    So when you declare a named type specialString with an underlying type of a predefined type string, your new type is a completely different type from the underlying one. This is because Go assumes you will want to assign different behaviors to your new type, and will not check its underlying type until run-time. This is why Go is both a static and dynamic language.

    When you print

    fmt.Println(reflect.TypeOf(ss))        // specialString
    

    You get specialString, not string. If you take a look at Println() the definition is as follows:

    func Println(a ...interface{}) (n int, err error) {
            return Fprintln(os.Stdout, a...)
    }
    

    This means you can print any predeclared types (int, float64, string) because all of them implements at least zero methods, which makes them already conform to the empty interface and pass as "printable", but not your named type specialString which remains unknown to Go during compile time. We can check by printing the type of our interface{} against specialString.

    type specialString string 
    type anything interface{}
    
    s := string("cheese")
    ss := specialString("special cheese")
    at := anything("any cheese")
    
    fmt.Println(reflect.TypeOf(ss))     // specialString
    fmt.Println(reflect.TypeOf(s))      // string
    fmt.Println(reflect.TypeOf(at))     // Wow, this is also string!
    

    You can see that specialString keeps being naughty to its identity. Now, see how it does when passed into a function at run-time

    func printAnything(i interface{}) {
            fmt.Println(i)
    }
    
    fmt.Println(ss.(interface{}))       // Compile error! ss isn't interface{} but
    printAnything(ss)                   // prints "special cheese" alright
    

    ss has become passable as interface{} to the function. By that time Go has already made ss an interface{}.

    If you really want to understand deep down the hood this article on interfaces is really priceless.

    0 讨论(0)
  • 2021-01-22 11:44

    I believe the initial authors' logic here is that named type is named for a reason - it represents something different, not just underlying type.

    I guess I've read it somewhere in golang-nuts, but can't remember exact discussion.

    Consider the following example:

    type Email string
    

    You named it Email, because you need to represent e-mail entity, and 'string' is just simplified representation of it, sufficient for the very start. But later, you may want to change Email to something more complex, like:

    type Email struct {
        Address string
        Name    string
        Surname string
    }
    

    And that will break all your code that work with Email implicitly assuming it's a string.

    0 讨论(0)
提交回复
热议问题