Rust中的Traits与Haskell中的类型类(typeclasses)表面上看起来相似,但是有人写道它们之间存在一些差异。我想知道这些差异具体是什么。
Functor
和Monad
类型类。值得一提的是,正如评论中所说,GHC(Haskell的主要编译器)支持更多的类型类选项,包括多参数(即涉及许多类型)类型类,以及功能依赖,这是一个很棒的选项,允许进行类型级计算,并引出类型族。据我所知,Rust 没有 funDeps 或类型族,但它可能会在未来加入†。
总之,特质和类型类有根本性的区别,由于它们的交互方式,使它们在最终的表现和行为上看起来非常相似。
在这里可以找到一篇关于Haskell类型类的好文章(包括高级类型),链接,Rust by Example中有关特性的章节可以在此处找到。
我认为当前的答案忽略了Rust traits和Haskell type classes之间最基本的区别。这些差异与traits与面向对象语言构造的关系有关。有关此信息,请参见Rust book。
A trait declaration creates a trait type. This means that you can declare variables of such a type (or rather, references of the type). You can also use trait types as parameters on function, struct fields and type parameter instantiations.
A trait reference variable can at runtime contain objects of different types, as long as the runtime type of the referenced object implements the trait.
// The shape variable might contain a Square or a Circle,
// we don't know until runtime
let shape: &Shape = get_unknown_shape();
// Might contain different kinds of shapes at the same time
let shapes: Vec<&Shape> = get_shapes();
This is not how type classes work. Type classes create no types, so you can't declare variables with the class name. Type classes act as bounds on type parameters, but the type parameters must be instantiated with a concrete type, not the type class itself.
You can not have a list of different things of different types which implement the same type class. (Instead, existential types are used in Haskell to express a similar thing.) Note 1
Trait methods can be dynamically dispatched. This is strongly related to the things that are described in the section above.
Dynamic dispatch means that the runtime type of the object a reference points is used to determine which method that is called though the reference.
let shape: &Shape = get_unknown_shape();
// This calls a method, which might be Square.area or
// Circle.area depending on the runtime type of shape
print!("Area: {}", shape.area());
Again, existential types are used for this in Haskell.
在我看来,特质在许多方面与类型类相同。此外,它们具有面向对象接口的功能。
另一方面,Haskell的类型类更加先进。例如,Haskell拥有更高级别的类型和扩展,如多参数类型类。
注1:Rust的最新版本有一个更新,以区分特质名称作为类型和特质名称作为约束条件的用法。在特质类型中,名称前缀为dyn
关键字。有关更多信息,请参见此答案。
dyn Trait
视为一种存在类型的形式。我们可以将dyn
视为一个约束的运算符,用于将其投射到类型上,即dyn:List Bound -> Type
。在Haskell中,考虑到“因此您无法使用类名称声明变量”,我们可以通过以下方式间接完成:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t
。定义完这个之后,我们就可以使用[D True, D "abc", D 42] :: [D Show]
进行操作了。 - CentrilRust的“traits”类似于Haskell的类型类。
与Haskell的主要区别在于,traits仅对带有点符号表示法的表达式起作用,即形式为a.foo(b)的表达式。
Haskell类型类扩展到高阶类型。 Rust traits之所以不支持高阶类型,是因为整个语言中缺少它们,即这不是traits和类型类之间的哲学差异。
Default
特质,它没有方法,只有非方法关联函数。 - CentrilRust的特质类似于Haskell的类型类。它们是将相似函数分组的一种方式。
Haskell和其他编程语言之间的主要区别在于,特质只适用于使用点符号表示的表达式,例如a.foo(b)。
在Haskell中,类型类可以扩展到高阶类型。这意味着您可以通过使用特质创建像语言中的其他类型一样行为的类型。Rust没有此功能,因为它没有高阶类型。
class Functor f where fmap :: (a -> b) -> (f a -> f b)
;后者的一个示例是class Bounded a where maxBound :: a
。 - Daniel Wagnerstd::default
),并且多参数特征有点可行(包括函数依赖的类比),尽管据我所知,需要解决第一个参数受到特殊待遇的问题。但是没有高阶类型。它们在远期愿望清单上,但目前还没有出现在视野中。 - user395760