为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
蛊毒传说 2021-09-13 19:52:38
考虑这个代码:package mainimport "fmt"type specialString stringfunc 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)in main()?(我不是在寻找指向 Golang 赋值规则的答案;我已经阅读了它们,并且我看到 specialString 和 string 都具有相同的“底层类型”并且两种类型都是“命名的”——如果你考虑要命名的泛型类型“字符串”,Golang 显然是这样做的——因此它们在规则下不可分配。)但是为什么会有这样的规则呢?通过将内置类型视为“命名”类型并阻止您将命名类型传递给所有接受相同底层内置类型的标准库函数,解决了什么问题?有人知道这里的语言设计者是怎么想的吗?从我的角度来看,它似乎在代码中创建了很多毫无意义的类型转换,并且不鼓励在真正有意义的地方使用强类型。
查看完整描述

2 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

我相信这里最初作者的逻辑是命名类型的命名是有原因的 - 它代表不同的东西,而不仅仅是底层类型。


我想我在 golang-nuts 的某个地方读过它,但不记得确切的讨论。


考虑以下示例:


type Email string

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


type Email struct {

    Address string

    Name    string

    Surname string

}

假设它是一个字符串,这将破坏所有与 Email 一起使用的代码。


查看完整回答
反对 回复 2021-09-13
?
PIPIONE

TA贡献1829条经验 获得超9个赞

这是因为 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,它仍然未知在编译期间去。我们可以通过打印 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!

你可以看到它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{}.


查看完整回答
反对 回复 2021-09-13
  • 2 回答
  • 0 关注
  • 191 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信