英文:
How to cast to a type alias in Go?
问题
请看**这个代码片段**。
相关代码:
type somethingFuncy func(int) bool
func funcy(i int) bool {
return i%2 == 0
}
var a interface{} = funcy
func main() {
_ = a.(func(int) bool) // 这个可以工作
fmt.Println("太棒了 - 显然,直接指定函数签名是可以的。")
_ = a.(somethingFuncy) // 这个会引发恐慌
fmt.Println("糟糕 - 没有执行到这里。但是somethingFuncy和func(int) bool是相同的签名。")
}
第一个类型转换可以工作,通过显式声明类型。但是第二个类型转换会引发恐慌。为什么?有没有一种干净的方法来将其转换为更长的函数签名?
英文:
Relevant code:
type somethingFuncy func(int) bool
func funcy(i int) bool {
return i%2 == 0
}
var a interface{} = funcy
func main() {
_ = a.(func(int) bool) // Works
fmt.Println("Awesome -- apparently, literally specifying the func signature works.")
_ = a.(somethingFuncy) // Panics
fmt.Println("Darn -- doesn't get here. But somethingFuncy is the same signature as func(int) bool.")
}
The first cast works, by explicitly declaring the type. But the second cast panics. Why? Is there a clean way to cast to a longer func signature?
答案1
得分: 91
tl;dr
对于类型断言(你使用的),只有实际类型才重要。所以 somethingFuncy
只等于 somethingFuncy
,而不等于 func(int) bool
。
解释
首先,这与类型转换无关。Go 中没有类型转换。有 类型断言 和 类型转换。
你正在处理类型断言,并假设与类型转换相同的条件成立。我在阅读你的问题时也犯了同样的错误,但实际上行为上有很大的差异。
假设你有两种类型,比如 int
和 type MyInt int
。它们是可转换的,因为它们共享相同的底层类型(转换规则之一),所以这样可以工作(play):
var a int = 10
var b MyInt = MyInt(a)
现在,假设 a
不是 int
类型,而是 interface{}
类型(play):
var a interface{} = int(10)
var b MyInt = MyInt(a)
编译器会告诉你:
无法将 a(类型 interface {})转换为类型 MyInt:需要类型断言
所以现在我们不再进行转换,而是进行断言。我们需要这样做(play):
var a interface{} = int(10)
var b MyInt = a.(MyInt)
现在我们遇到了与你的问题相同的问题。这个断言会引发以下恐慌:
panic: interface conversion: interface is int, not main.MyInt
造成这种情况的原因在规范的 类型断言部分 中说明:
对于接口类型的表达式 x 和类型
T
,主表达式x.(T)
断言x
不是nil
,并且存储在x
中的值是类型T
。
更准确地说,如果T
不是接口类型,则x.(T)
断言x
的动态类型与类型T
相同。
所以 int
必须与 MyInt
相同。类型标识 的规则说明(除其他规则外):
如果两个命名类型的类型名称源自相同的 TypeSpec,则它们是相同的。
由于 int
和 MyInt
有不同的声明(TypeSpecs),它们不相等,断言失败。当你将 a
断言为 int
时,断言成功。所以你正在做的是不可能的。
附加信息:
实际的检查发生在此代码中,它简单地检查两个类型是否相同,正如预期的那样。
英文:
tl;dr
For type assertions (which you use) only the actual type matters. So somethingFuncy
is only equal to somethingFuncy
and not to func(int) bool
.
Explanation
To start with, this has nothing to do with casting. There is no casting in go.
There are type assertions and type conversions.
You're dealing with a type assertion and are assuming that the same conditions hold
as for type conversions. I made the same mistake while reading your question but actually there is a huge difference in behaviour.
Suppose you have two types, say int
and type MyInt int
. These are convertible as they both
share the same underlying type (one of the conversion rules), so this works (play):
var a int = 10
var b MyInt = MyInt(a)
Now, suppose a
is not of type int
but of type interface{}
(play):
var a interface{} = int(10)
var b MyInt = MyInt(a)
The compiler will tell you:
> cannot convert a (type interface {}) to type MyInt: need type assertion
So now we're not doing conversions anymore but assertions. We need to do this (play):
var a interface{} = int(10)
var b MyInt = a.(MyInt)
Now we have the same problem as in your question. This assertion fails with this panic:
> panic: interface conversion: interface is int, not main.MyInt
The reason for this is stated in the type assertions section of the spec:
> For an expression x of interface type and a type T
, the primary expression x.(T)
> asserts that x
is not nil
and that the value stored in x
is of type T
. The notation
> x.(T)
is called a type assertion.
> More precisely, if T
is not an interface type, x.(T)
asserts that the dynamic
> type of x
is identical to the type T
.
So int
must be identical to MyInt
. The rules of type identity state that
(amongst other rules):
> Two named types are identical if their type names originate in the same TypeSpec.
As int
and MyInt
have different declarations (TypeSpecs) they're not equal
and the assertion fails. When you assert a
to int
, the assertion works.
So what you're doing is not possible.
Bonus:
The actual check happens in this code, which simply checks if both types are
the same, as expected.
答案2
得分: 32
更新于2017年:
在Go 1.9中,通过类型断言,你可以在定义类型时简单地添加=
。
type somethingFuncy = func(int) bool
这告诉编译器somethingFuncy
是func(int) bool
的另一个名称。
英文:
Update 2017:
With type assertions in Go 1.9, you can simply add =
where you define the type.
type somethingFuncy = func(int) bool
This tells the compiler that somethingFuncy
is an alternate name for func(int) bool
.
答案3
得分: 8
只是为了完善nemo的精彩回答,需要注意的是,虽然你不能直接从一个接口(例如,interface{}
)的动态类型(例如,int
)跳转到另一个类型(例如,type MyInt int
),但你可以按顺序执行两个步骤:
- 断言你的变量的动态类型是你期望的类型;
- 将断言的结果转换为你选择的类型。
需要注意的是,由于底层类型是动态的,正如其名称所暗示的那样,测试类型断言是否成功或失败是一个好主意。
另一方面,类型转换的正确性由编译器强制执行。
这是稍作修改的你的代码片段:http://play.golang.org/p/FZv06Zf7xi
英文:
Just to complete nemo's awesome answer, note that while you can't jump directly from an interface (e.f., interface{}
) of a given dynamic type (e.g., int
) to another type (e.g., type MyInt int
), you can do the two steps one after the other:
- assert that the dynamic type of your variable is what you expect it to be;
- convert the result of that assertion to the type of your choice.
Note that since the underlying type is, as its name implies, dynamic, it's a good idea to test if the type assertion succeeded or failed.
On the other hand, the type conversion correctness is enforced by the compiler.
Here is your playground snippet slightly modified: http://play.golang.org/p/FZv06Zf7xi
答案4
得分: 2
我相信你想要的是类型别名。该提案已被接受,并应该在Go 1.9中实现。即:
TypeSpec = identifier ["="] Type.
参考资料:
https://github.com/golang/go/issues/18130
https://github.com/golang/proposal/blob/master/design/18130-type-alias.md
英文:
I believe that type alias is what you want. Proposal is accepted and should be in Go 1.9. Ie.
TypeSpec = identifier [ "=" ] Type .
References<br>
https://github.com/golang/go/issues/18130<br>
https://github.com/golang/proposal/blob/master/design/18130-type-alias.md
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论