在Go语言中,只有未命名的别名类型才可以进行赋值操作。

huangapple go评论80阅读模式
英文:

Aliased types in Go only assignable if unnamed?

问题

在下面的代码片段中,最后三个赋值语句会产生编译错误:

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规范:可赋性)

以及

[...] 命名类型由(可能是限定的)类型名称指定;未命名类型使用类型字面量来指定,它从现有类型组合成一个新类型。[...]

(Go规范:类型)

综合起来,为什么别名的赋值不能工作是因为最后三种情况的类型是命名类型。通过这个规则,违反了两个命名类型参与赋值的规则。

现在是我真正的问题:

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

缺乏这个规则会导致哪些问题?

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

提前谢谢你的回答。

英文:

In the following code snippet the last three assignments produce a compilation error:

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
	)
}

This means that, in this example, only bools, strings and floats are not assignable.
The reason for this can be found in the specification:

> A value x is assignable to a variable of type T ("x is assignable to
T") in any of these cases:
>
> - [...]
> - x's type V and T have identical underlying types and at least one of V or T is not a named type.
> - [...]

(Go Specification: Assignability)

and

> [...] Named types are specified by a (possibly qualified) type name; unnamed types are specified using a type literal, which composes a new type from existing types. [...]

(Go Specification: Types)

Combining this, the reason why the aliased assign does not work is because the types of the last three cases are named. Through this the rule is violated: Two named types are part of the assignment.

Now to my actual question(s):

Why should it not be allowed to assign an aliased string/bool/numeric to an actual string/bool/numeric, as opposed to types like slices and arrays?

What kinds of problems would the lack of this rule cause?

What kinds of problems would the specification of a string as a named type cause?

Thank you in advance.

答案1

得分: 0

可赋值性规则意味着有时你需要在命名类型之间进行转换,明确地表示“是的,我希望将这个string作为Foo使用”,即使它们共享相同的基础类型。这对于像os.FileMode这样的东西很重要:它在底层是一个数字,但类型检查会防止你意外地将其传递给接受不相关的foo uint32的函数。(可赋值性规则也会影响函数调用:你可以传递任何可赋值给参数类型的类型的参数。)

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

因此,总体上,通过强制你在命名类型之间进行转换,Go帮助你区分那些在内存中可能具有相同表示但具有不同含义并且应该在不同位置使用的东西。

英文:

The assignability rules mean you sometimes have to convert between named types to explicitly say "yes, I mean this string to be used as a Foo," even if they share the same underlying type. This is relevant to things like os.FileMode: it's a number underneath, but type checks will keep you from accidentally passing it to a function that takes an unrelated foo uint32. (Assignability rules affect function calls, too: you can pass an argument of any type that's assignable to the parameter type.)

Generally, this means if you have distinct, potentially confusable uses for an underlying type, you can assign different names to them. Like: the underlying type [][4]float32 could plausibly have RGBASlice, HSVASlice, and XYZWSlice type names assigned. You can't pass a RGBASlice to a function that expects an XYZWSlice. But all can be passed transparently to things that don't care about the meanings of the numbers, like if you have some all-purpose vector-math routines.

So, broadly, by forcing you to convert between named types, Go helps you distinguish between things that might have the same representation in memory even though they have different meanings and are supposed to be used different places.

huangapple
  • 本文由 发表于 2014年12月24日 09:44:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/27630418.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定