如何在函数中使用递归关联类型?

时间:2017-01-28 15:02:46

标签: rust

我正在尝试编写一个可以下降到任何类型的Value的函数,并告知Delegate它所观察到的相似之处。我们的想法是通过各种Json / Yaml / YouNameIt值来完成这项工作。

这是一个触发问题的MVCE(playground link):

pub trait Value: PartialEq<Self> {
    type Item;
    type Key;
    fn items<'a>(&'a self) -> Option<Box<Iterator<Item = (Self::Key, &'a Self::Item)> + 'a>>;
}

pub trait Delegate<'a, V> {
    fn something(&mut self, _v: &'a V) {}
}

pub fn diff<'a, V, D>(l: &'a V, d: &'a mut D)
    where V: Value,
          <V as Value>::Item: Value,
          D: Delegate<'a, V>
{
    d.something(l);
    let v = l.items().unwrap().next().unwrap();
    d.something(v.1);
}

struct Recorder;
impl<'a, V> Delegate<'a, V> for Recorder {}

#[derive(PartialEq)]
struct RecursiveValue;

impl Value for RecursiveValue {
    type Key = usize;
    type Item = RecursiveValue;
    fn items<'a>(&'a self) -> Option<Box<Iterator<Item = (Self::Key, &'a Self::Item)> + 'a>> {
        None
    }
}

fn main() {
    let v = RecursiveValue;
    let mut r = Recorder;
    diff(&v, &mut r);
}    

尝试编译代码时,会产生以下错误:

error[E0308]: mismatched types
  --> <anon>:19:17
   |
19 |     d.something(v.1);
   |                 ^^^ expected type parameter, found associated type
   |
   = note: expected type `&'a V`
   = note:    found type `&<V as Value>::Item`

我试图说相关的Item类型也属于V类型。有没有办法让这种算法一般工作?

2 个答案:

答案 0 :(得分:2)

答案在于Rust书的Associated Types章的最底层。

在绑定中使用泛型类型时,如V: Value中所述,可以使用Generic<AssociatedType = SpecificType>语法将一个或多个关联类型约束为特定类型。

在您的情况下,这意味着将V限制为Value<Item = V>。这也应该淘汰任何进一步限制V::Item的理由,因为V的界限是自然可用的。

我鼓励你阅读这本书,以帮助你学习Rust,或者至少浏览它以了解那里有什么,并且当你遇到困难时可以参考它。

答案 1 :(得分:1)

这是一个进一步缩小的例子:

pub trait Value {
    type Item;
    fn items(&self) -> &Self::Item;
}

pub trait Delegate<V> {
    fn something(&mut self, v: &V);
}

pub fn diff<V, D>(l: &V, d: &mut D)
    where V: Value,
          V::Item: Value,
          D: Delegate<V>
{
    let v = l.items();
    d.something(v);
}

fn main() {}

要关注的重点是对diff

的泛型的限制
pub fn diff<V, D>(l: &V, d: &mut D)
    where V: Value,
          V::Item: Value,
          D: Delegate<V>

用语言来说,这说:

  • V可以是任何类型,只要它实现Value特征。
  • V::Item可以是任何类型,只要它实现Value特征。
  • D可以是任何类型,只要它实现Delegate<V>特征。

列出的要求列表中没有任何地方&#34; VV::Item必须相同&#34;。事实上,它是一个功能,它们不需要是相同的。

缩减中,另一种解决方案是D: Delegate<V::Item>。但是,这不适用于稍大的再现:

pub fn diff<V, D>(l: &V, d: &mut D)
    where V: Value,
          V::Item: Value,
          D: Delegate<V::Item>
{
    d.something(l);
    let v = l.items();
    d.something(v);
}

作为Matthieu M. has pointed out,您要指定特征的关联类型

pub fn diff<V, D>(l: &V, d: &mut D)
    where V: Value<Item = V>,
          D: Delegate<V>

如需进一步阅读,请查看Requiring implementation of Mul in generic function