我刚刚了解到使用泛型的结构体被声明为struct Foo<T>
,并且实现被声明为impl<T> Foo<T>
。 book以下列方式解释了差异:
请注意,我们必须在
T
之后声明impl
才能在T
类型中使用Point<T>
。在T
之后将impl
声明为泛型类型是Rust知道Point
中尖括号中的类型是泛型类型而不是具体类型。
同样,我是一个新手,但是因为必须宣布struct<T> Foo<T>
(甚至可能是fn<T> foo<T>(bar: T)
)而无法做出相同的论证?
答案 0 :(得分:7)
impl Foo<T> { ... }
T
是类型参数,还是名为T
的实际类型?如果您回答了第二个问题,那么您已经完全抛弃了泛型:不可能为一组通用类型实现功能。如果你回答了第一个,那么你如何建议用户这样做:
impl Foo<T> { /* generic stuff */ }
impl Foo<i32> { /* i32-specific stuff */ }
impl Foo<u32> { /* u32-specific stuff */ }
这只是三个impl
块,其类型参数分别为T
,i32
和u32
。
你可能会说“好吧,只是从上下文决定”。现在,为了弄清楚发生了什么,用户不仅要检查模块的其余部分,还要检查所有导入的符号和标准库的前奏中的任何内容,以便确定类型是否为参数与否。鲁斯特厌恶这种模糊性。
编译器和读者只需具体了解什么是参数而不是参数,这是远远不够的远。
您不必对struct
执行此操作,因为括号中的内容不可能是其他而不是类型参数声明。
答案 1 :(得分:1)
除了@dk的答案之外,我想比较一下Rust如何做到Haskell的做法。
在Haskell中,所有特定类型都必须开始大写。因此,写下来是违法的:
data mytype Var = Constructor {
myVar :: Var,
myInt :: Foo
}
相反,Haskell保留的类型以类型变量的小写字母开头:
data MyType var = Constructor {
myVar :: var
myInt :: Int
}
instance MyClass (MyType var) where
...
此处,var
是一个类型变量,MyType
是具体的,Int
是具体的。
在我看来 - 你可能不会分享,Rust犯了错误,允许使用非大写的类型标识符。因此,该语言别无选择,只是要求您明确说明哪些类型标识符是变量,哪些是特定的。可以认为Rust的显性提高了可读性。相反也可以争论。
Rust当然可以添加一个假设的功能,您可以说:
impl Foo<#T, T, u32> { .. }
此处,前缀#
表示“这是一个类型变量”,因此#T
是一个类型变量,T
是特定的,u32
是具体。没有歧义,只需要本地推理。
有了这个功能,你可能会得到一点点冗长,但我坚信这艘船已经航行了 - 现在添加这个功能只会增加另一种方式来做同样的事情而获得的收益很少。但是,该功能会增加使用语言用户必须学习的其他功能的成本。