yaw*_*awn 1 polymorphism enums traits subtype rust
我有两种类型,A并且B,它们共享一个通用的接口T.我想编写可以同时使用A和B通过接口工作的函数T.
在C++中,我会T是两者的一个抽象超A和B,和写入功能接受类型的参数T.
在Rust,似乎我有两个很好的解决方案.
enum T {
A { /* ... */ },
B { /* ... */ },
}
impl T {
fn method(&self) {
match *self {
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
在方法中T,我可以匹配变体以专门化类型,然后在访问变体成员时写下我想要的任何内容.然后我可以编写T直接接受类型参数的函数:
fn f(t: T) {
// I can use t.method here
}
Run Code Online (Sandbox Code Playgroud)
trait T {
// ...
}
struct A { /* ... */ }
struct B { /* ... */ }
impl T for A {
fn method(&self) {
// ...
}
}
impl T for B {
fn method(&self) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
对于traits,类型已经专门用于方法定义.但是,要使用T.method,我需要编写一个通用函数:
fn f<X: T>(t: X) {
// I can use t.method here
}
Run Code Online (Sandbox Code Playgroud)
让我困扰的是,虽然这两种解决方案都能很好地工作,但实现却暴露出来:f两个例子中的签名都不同.如果我用枚举编写一个库,但最终决定我真正想要的是特征,那么每个用户级类型的签名都需要改变!
鉴于这一事实,图书馆作者选择了什么选择,为什么?
请注意,我并不特别关心继承,我不打算将C++习惯用法导入Rust,我试图了解更好的替代方案.
图书馆作者喜欢使用枚举或特征吗?
建筑商是否喜欢使用钉子或螺钉?
医生是否喜欢使用胶水,缝线或钉书钉?
这是一个荒谬的二分法.两种功能都存在并且都使用.人们使用正确的工具来完成手头的特定工作.
首先回过头来重新阅读Rust编程语言一章是Rust是面向对象的编程语言吗?.
简而言之,特征允许无限多态,而枚举是严格限制的.真的,这是主要的区别.查看您的问题域需要什么并使用正确的东西.
实现暴露:
f两个示例中的签名都不同
是的,设计软件有时需要一些前期的思考,关怀和设计.
如果向用户公开枚举,然后添加,删除或修改变体,则需要更新该枚举的每个用法.
如果向用户公开特征,然后添加,删除或修改方法,则需要更新该特征的每个用法.
你选择导致这个问题的枚举或特征这一事实并没有什么特别之处.您还可以重命名方法,添加,删除或重新排序参数.有很多方法可以给用户带来麻烦.
如果您更改API,您的用户将受到影响.
我需要编写一个通用函数
您不需要,但出于性能原因,您可能应该默认使用它.您可以使用特征对象fn f(t: &T),fn f(t: Box<T>)如果<T>它引起惊慌.
用枚举写一个库,但最终决定我真正想要的是特质
既然你知道你不必总是选择其中一个,也意识到你可以一起使用它们:
enum Pet {
Cat,
Dog,
Other(Box<Animal>),
}
trait Animal {}
Run Code Online (Sandbox Code Playgroud)
trait TimeSource {
fn now() -> u64;
}
enum Builtin {
Ntp,
Sundial,
}
impl TimeSource for Builtin {
// ...
}
Run Code Online (Sandbox Code Playgroud)
个人观点方面,如果我仍然是我的代码原型并且不清楚哪个选择更好,我可能会默认使用特征.无界多态性更符合我对依赖注入和测试风格的偏好.