当一个是本机类型时,为什么golang禁止分配给相同的底层类型?

时间:2015-02-20 17:20:32

标签: go

考虑以下代码:

package main
import "fmt"

type specialString string

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

// unlike, say, C++, this is not legal GO, because it redeclares printString
//func printString(s specialString) {    
//  fmt.Println("Special: " + s)
//}

func main() {
    ss := specialString("cheese")
    // ... so then why shouldn't this be allowed?
    printString(ss)
}

我的问题是:为什么是定义的语言,因此不允许调用printString(ss)中的main()? (我没有找到指向Golang规则的答案;我已经阅读过了,我看到specialString和string都有相同的基础类型'两种类型都是& #39;命名' - 如果您考虑要命名的泛型类型'字符串' Golang显然也​​是如此 - 因此它们不能根据规则分配。)

为什么是这样的规则?通过将内置类型视为名为'来解决什么问题?类型,并阻止您将命名类型传递给接受相同底层内置类型的所有标准库函数?有人知道语言设计师在这里想到了什么吗?

从我的角度来看,它似乎在代码中创建了许多无意义的类型转换,并且不鼓励使用强类型,而实际上它是有意义的。

2 个答案:

答案 0 :(得分:5)

我相信最初的作者'这里的逻辑是命名类型是出于某种原因而命名的 - 它表示不同的东西,而不仅仅是基础类型。

我想我已经在golang-nuts中读到了它,但不记得确切的讨论。

考虑以下示例:

type Email string

您将其命名为电子邮件,因为您需要代表电子邮件实体,并且'字符串'只是它的简化表示,一开始就足够了。但是稍后,您可能希望将电子邮件更改为更复杂的内容,例如:

type Email struct {
    Address string
    Name    string
    Surname string
}

这会破坏所有使用电子邮件的代码,隐含地假设它是一个字符串。

答案 1 :(得分:2)

这是因为Go没有类继承。它使用结构组合代替。命名类型不会从其基础类型继承属性(这就是它不被称为“基类型”的原因)。

因此,当您声明具有预定义类型specialString的基础类型的命名类型string时,您的新类型与基础类型完全不同。这是因为Go假定您希望为新类型分配不同的行为,并且在运行时之前不会检查其基础类型。这就是Go是一种静态和动态语言的原因。

打印时

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

您获得specialString,而不是string。如果你看一下Println(),定义如下:

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

这意味着您可以打印任何预先声明的类型(int,float64,string),因为它们都实现了至少为零的方法,这使得它们已经符合空接口并传递为“可打印” ,但不是您的命名类型specialString,在编译期间Go仍然不知道。我们可以针对interface{}打印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!

你可以看到specialString对其身份一直保持顽皮。现在,看看它在运行时传递给函数时的作用

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已作为interface{}传递给该函数。到那时,Go已经ss成了interface{}

如果你真的想深入了解这个问题,article on interfaces真是无价之宝。