我想抽象出各种不同的列表数据结构。我的抽象应该相当灵活。我想要一个“基本特征”(我们称其为List
),它代表所有数据结构所需的最小接口。 但是,数据结构可以提供可选功能,它们彼此独立:
Trait
和TraitMut
(例如Index
和IndexMut
)。最初,这似乎很容易:提供特征List
,ListMut
,FooList
和BarList
,其中后三个具有List
作为超级特征。像这样:
trait List {
fn num_elements(&self) -> usize;
}
trait ListMut: List {
fn clear(&mut self);
}
trait FooList: List {
fn num_foos(&self) -> usize;
}
trait BarList: List {
fn num_bars(&self) -> usize;
}
这对于上面的方法很好用。 但是重要的是,有些方法需要 multiple 功能。例如:
add_foo(&mut self)
:需要可变性和功能“ foo”!add_foo_and_bar(&mut self)
:需要可变性和功能“ foo” 和“ bar”。具有多个要求的方法应该放在哪里?
一种方法是为可选要求的每种组合额外创建一个特征:
FooListMut
BarListMut
FooBarList
FooBarListMut
这些特征将具有适当的超级特征界限,并且可以容纳具有多个要求的方法。这样做有两个问题:
ListMut
和FooList
的类型也实现FooListMut
。因此,函数可能需要添加更多界限。这种特质系统将为实施者提供我可能不想给他们的灵活性。where Self
的方法范围还可以向方法添加where Self: Trait
范围。例如:
trait FooList: List {
// ...
fn add_foo(&mut self)
where
Self: ListMut;
}
这也可以,但是有两个重要的缺点:
FooList
的{{1}}的实现者将需要虚拟实现ListMut
(通常使用add_foo
),因为the Rust compiler still requires it。 unreachable!()
也可以生活在add_foo
内,边界为ListMut
。这使得特征API更加令人困惑。在此解决方案中,只会有一个特征。 (请注意,在下面的代码中使用了伪类型。理想情况下,这将是关联的where Self: FooList
而不是const
,但是我们还不能在特征范围内使用const,因此是伪类型。)
type
这解决了两个问题:首先,我们知道如果支持trait Bool {}
enum True {}
enum False {}
impl Bool for True {}
impl Bool for False {}
trait List {
type SupportsMut: Bool;
type SupportsFoo: Bool;
type SupportsBar: Bool;
fn add_foo(&mut self)
where
Self: List<SupportsMut = True, SupportsBar = True>;
// ...
}
和Mut
,我们可以使用Foo
(与第一个解决方案不同,第一个解决方案是数据结构可以实现add_foo
和ListMut
,但不能实现FooList
)。而且,由于所有方法都具有一种特质,因此,现在尚不清楚方法应存在于何处。
但是:
FooListMut
实现。
unreachable!()
(对于foo和bar来说也是一样)作为特征别名(带有毯子隐含符号),以使其变得更好一些。到目前为止,我可以想到三种解决方案。当然,可以将它们组合在一起。也许甚至还有完全不同的解决方案?
一个解决方案比其他解决方案有明显的优势吗?它们有重要的语义差异吗?社区是否认为其中之一更惯用?以前有没有对此的讨论?应该首选哪一个?