通过特征对类型的许多可选和独立功能进行抽象的最惯用/最好的方法是什么?

时间:2019-05-15 13:31:55

标签: rust traits

我想抽象出各种不同的列表数据结构。我的抽象应该相当灵活。我想要一个“基本特征”(我们称其为List),它代表所有数据结构所需的最小接口。 但是,数据结构可以提供可选功能,它们彼此独立:

  • 某些数据结构允许突变,而其他数据结构仅提供只读接口。对于Rust特征来说,这是相当普遍的:成对的TraitTraitMut(例如IndexIndexMut)。
  • 某些数据结构提供了“ foo”功能。
  • 某些数据结构提供了功能“栏”。

最初,这似乎很容易:提供特征ListListMutFooListBarList,其中后三个具有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

这些特征将具有适当的超级特征界限,并且可以容纳具有多个要求的方法。这样做有两个问题:

  • 特征的数量将随着可选功能的数量呈指数增长。是的,特征只需要和方法一样多,但是仍然可以导致非常混乱的API,其中包含大量特征,其中大多数特征仅包含一个/少数方法。 li>
  • (我认为)没有办法强迫实现ListMutFooList的类型也实现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_fooListMut,但不能实现FooList)。而且,由于所有方法都具有一种特质,因此,现在尚不清楚方法应存在于何处。

但是:

    与上一个解决方案一样,实现者仍然可能需要添加一堆FooListMut实现。
  • 绑定某些功能会更加嘈杂。不过,可以添加unreachable!()(对于foo和bar来说也是一样)作为特征别名(带有毯子隐含符号),以使其变得更好一些。

还有什么吗?

到目前为止,我可以想到三种解决方案。当然,可以将它们组合在一起。也许甚至还有完全不同的解决方案?


一个解决方案比其他解决方案有明显的优势吗?它们有重要的语义差异吗?社区是否认为其中之一更惯用?以前有没有对此的讨论?应该首选哪一个?

0 个答案:

没有答案