【问题标题】:Aliased types in Go only assignable if unnamed?Go中的别名类型只有在未命名的情况下才能分配?
【发布时间】:2015-02-22 04:32:20
【问题描述】:

在以下代码中,sn -p 最后三个赋值产生编译错误:

package main

type (
    Foo []float64
    Baz [2]float64
    Meh map[string]string
    Faq chan int
    Tet func()
    Hue interface{}
    Tai bool
    Foz string
    Bar float64
)

func main() {
    var ( 
        foo Foo = []float64{1, 2, 3}
        _ []float64 = foo

        baz Baz = [...]float64{1, 2}
        _ [2]float64 = baz

        meh Meh = make(map[string]string)
        _ map[string]string = meh

        faq Faq = make(chan int)
        _ chan int = faq

        tet Tet = func() { return }
        _ func() = tet

        hue Hue = "Hello, World"
        _ interface{} = hue

        tai Tai = true
        _ bool = tai // error

        foz Foz = "Hello, World"
        _ string = foz // error

        bar Bar = 1
        _ float64 = bar // error
    )
}

这意味着,在这个例子中,只有布尔值、字符串和浮点数是不可赋值的。 其原因可以在规范中找到:

值 x 可分配给 T 类型的变量(“x 可分配给 T") 在任何这些情况下:

  • [...]
  • x 的类型 V 和 T 具有相同的基础类型,并且 V 或 T 中的至少一个不是命名类型。
  • [...]

(Go Specification: Assignability)

[...] 命名类型由(可能是限定的)类型名称指定;未命名类型使用类型文字指定,该类型文字由现有类型组成新类型。 [...]

(Go Specification: Types)

结合这一点,别名assign不起作用的原因是因为命名了最后三种情况的类型。这样就违反了规则:两个命名类型是赋值的一部分。

现在是我的实际问题:

为什么不允许将别名字符串/布尔值/数字分配给实际的字符串/布尔值/数字,而不是像切片和数组这样的类型?

缺少这条规则会导致什么样的问题?

将字符串指定为命名类型会导致哪些类型的问题?

提前谢谢你。

【问题讨论】:

  • 你当然可以问“为什么我不能有不同的语言?” Stack Overflow 上的问题,答案可能是“看起来你可以,现在你只需要实际构建它。”不过,比起学术问题,我更喜欢回答帮助人们构建事物的问题。
  • @twotwotwo 嗯,我问这个问题的主要原因是因为我知道 Go 的设计者花了很多心思在它上面,所以在这样的决定背后大概有某种想法,我别看。很抱歉,这个问题不是为了解决特定问题,而是更多关于语言设计的问题(总的来说,因为我认为 Go 不是唯一强制执行此规则的语言)。我在这里问是希望比我更了解语言设计的人能够详细说明为什么事情通常会以这种方式完成。
  • 我认为有一种方法可以解决这个编程问题(“我如何使用两个命名类型的不可分配性?),我正在试一试。

标签: types go assign


【解决方案1】:

可分配性规则意味着您有时必须在命名类型之间进行转换以明确地说“是的,我的意思是这个string 被用作Foo”,即使它们共享相同的底层类型。这与os.FileMode 之类的东西有关:它是下面的一个数字,但类型检查可以防止你意外地将它传递给一个接受不相关foo uint32 的函数。 (可分配性规则也会影响函数调用:您可以传递可分配给参数类型的任何类型的参数。)

通常,这意味着如果您对基础类型有不同的、可能容易混淆的用途,您可以为它们分配不同的名称。比如:底层类型[][4]float32 可能有RGBASliceHSVASliceXYZWSlice 分配的类型名称。您不能将RGBASlice 传递给需要XYZWSlice 的函数。但是所有东西都可以透明地传递给不关心数字含义的东西,比如你有一些通用的向量数学例程。

因此,概括地说,通过强制您在命名类型之间进行转换,Go 可以帮助您区分在内存中可能具有相同表示的事物,即使它们具有不同的含义并且应该在不同的地方使用。

【讨论】:

  • 如果您真的很喜欢花哨的类型技巧,请查看 ML 或 Rust 之类的语言。相比之下,Go 试图保持基本的东西。
  • 首先,谢谢。其次,这是否本质上意味着 Go 声明为命名类型的类型在实践中更容易在别名时与其他东西混淆?您的回答使可分配性的严格性似乎是命名类型的优势(“[...],但类型检查将防止您意外将其传递给采用不相关 foo uint32 的函数”),但同时,倒置参数就像是未命名类型的优势(“但所有都可以透明地传递给不关心数字含义的事物 [...]”)。
  • 嗯。我认为这里的 a 方式是你命名一个类型来声明除了组件之外还有一些特殊的含义。 [][4]float32 是 float32 数组的 any 切片,没有其他含义,因此它会很高兴地采用 RGBASliceRGBASliceXYZWSlice do 具有不同的含义,如果您想将一个作为另一个传递,则需要进行转换以告诉编译器这是故意的。
  • 我并不是说作者是这么想的,或者说创建一种未命名命名分配需要显式转换的语言会很疯狂。这只是我可能会考虑何时使用[][4]float32RGBASlice 的一种方式,如果我正在编写同时处理这两种方法的代码。
  • 编译器仍然将uint32FileMode 视为不可分配但不是[][4]float32RGBASlice——这在实践中确实有效,但没有关于为什么会这样的宏大理论结果本质上是正确的。
猜你喜欢
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 2022-10-06
  • 2013-07-06
  • 2018-07-11
  • 1970-01-01
  • 2019-06-21
  • 2015-07-29
相关资源
最近更新 更多