Rust递归类型难度

时间:2016-10-10 10:11:07

标签: rust

此代码简化了更复杂的代码以隔离问题:

use std::marker::PhantomData;

pub trait ExtWrite {
    fn write_end<W>(&mut self, &mut W);
}

pub struct ST;

impl ExtWrite for ST {
    fn write_end<W>(&mut self, _: &mut W) {}
}

struct MCW<'a, 'b, W: 'a, EW: 'b + ExtWrite>(&'a mut W, &'b mut [EW]);

impl<'a, 'b, W: 'a, EW: 'b + ExtWrite> MCW<'a, 'b, W, EW> {
    fn write_end_all(&mut self) {
        if let Some((f, last)) = self.1.split_first_mut() {
            let mut el = MCW(self.0, last);
            f.write_end(&mut el);
            // do on current el.write_end();
        }
    }
}

pub fn rec_test() {
    let mut buff = ();
    let v: Vec<TSW<ST>> = Vec::new();
    let mut el: TSW<ST> = TSW(Box::new(v), PhantomData);
    el.write_end(&mut buff);
}

pub struct TSW<E: ExtWrite>(Box<Vec<TSW<E>>>, PhantomData<E>);

impl<E: ExtWrite> ExtWrite for TSW<E> {
    fn write_end<W>(&mut self, w: &mut W) {
        let mut el = MCW(w, &mut self.0[..]);
        el.write_end_all();
    }
}

fn main() {}

导致以下错误:

error: reached the recursion limit while instantiating `<TSW<E> as ExtWrite><ST>::write_end::<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<(), TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>>`
  --> <anon>:40:3
   |
40 |   fn write_end<W>(&mut self, w: &mut W) {
   |   ^

我每晚都在使用Rust(9c31d76e9 2016-10-03)。

代码是一个结构,包含指向同一结构类型数组的Vec的指针。这些复杂的数组被递归调用以在编写器中应用一些写入(W特征约束被删除,因为它与该问题无关),并且在实际代码中ExtWrite变为Writer在某些情况下。

有一些地方特性解析变得时髦导致类型的递归,当考虑W特征的分辨率中的单态时,递归似乎相当合乎逻辑。 MCW,取决于递归的深度,将包含无数个可能的类型,但这实际上与MCW的使用(原始代码中需要)以及{{{{{{ 1}} W函数的参数未链接到结构定义,但具有此函数的无限可能变体。

然而,在此代码段中,read_end始终为W()应始终为MCW

我在寻找简化时遇到的类似情况:

MCW<(),TSW<ST>>

导致:

struct IntSt<'a, W: 'a>(&'a W);

pub fn comp_err() {
    let w: u8 = 0;
    rec(true, &w);
}

pub struct A(bool);

fn rec<W>(b: bool, w: &W) {
    if (b) {
        rec(false, &IntSt(w).0);
        //      rec(false, w)
    }
}

fn main() {}

它的行为正确,但我真的没有看到如何在我之前的案例中做出这种改变:

error: reached the recursion limit while instantiating `rec::<&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&u8>`
  --> <anon>:14:1
   |
14 | fn rec<W>(b: bool, w: &W) {
   | ^

它看起来像struct IntSt<'a, W: 'a>(&'a W); pub fn comp_err() { let w: u8 = 0; rec(true, &w); } pub struct A(bool); fn rec<W>(b: bool, w: &W) { if (b) { rec(false, IntSt(w).0); // rec(false, w) } } fn main() {} ,一个用作临时作家的轻型结构,导致其生命周期复杂化。这只发生在递归的情况下。这似乎真的很边缘,我不知道它是更多的错误或预期的限制。我也尝试使用排名较高的特质界限,但在这种情况下,我们真的在结构上工作,我的几次尝试都没有成功。

我可以简单地重新设计MCW(在我的实际情况中,我只在一个级别上有一个包含TSW Vec的结构的可选指针)并引入TSW没有指向TSW_2的指针,但它会真的感觉不好(至少不满意)作为解决方案。

- 编辑

是的,感谢Matthieu,这是正确的诊断(MCW误导了我(测试时我将其移除并且一切正常,但它没有做同样的事情):

Vec

事实上,在考虑原始问题时,类型应该完全正确 ...... MCW&lt; MCW&lt;(),TSW,&gt;叠加N次,N是矢量的大小。 (vec中的元素是Writers扩展,它应该适用于Vec的先前元素(分层编写者的类型))。

回顾我记得第一个问题是链接我的W然后通过使用vec来存储它们来解决它(然后我得到了单个Type,带有迭代的多层写法),但是后来我需要用这个Vec写一些以前Vec中的有效负载,这里我应该使用相同的推理和双数组)。但我这样做的方法是简单地尝试使用胖指针覆盖可选结构。这并不顺利,因为我做了类似“Option&lt; Box&lt; otherstructcontainingvec&gt;&gt;”的事情。但是包含vec的其他结构是没有特征的,同样在这个示例代码中,Vec也没有特征。

所以我希望我终于得到了我的解决方案:使用Vec&lt; TSW&lt; E&gt;&gt; (我的结构包含它在实际代码中)作为特征并且有一个真正的胖指针(更优化的解决方案将是双维vec(但我的用例实际上是单个重叠)。

1 个答案:

答案 0 :(得分:7)

你有一个乒乓球在这里,看不到尽头。

  • 致电TSW<E>::write_end<W>(&mut self, w : &mut W)
  • 调用MCW<W, TSW<E>>::write_end_all(&mut self)
  • 调用TSW<E>::write_end<MCW<W, TCW<E>>>(&mut self, w: &mut )
  • 调用...

每个新级别的递归都会在新类型上堆积,这就是为什么错误消息中的类型如此之大。 rustc编译器,而不是进入无限循环,告诉你,你可能不想实例化无数个函数。

这里的逻辑有问题。那不是一辈子的事。

当开始递归时,你需要一个退出策略的计划:它应该以可预测的方式结束。

在泛型类型的递归的特定情况下,它应该可以预测地结束参数的运行时值。

不知道正确的逻辑应该是什么,我给你一个迭代的解决方案:

fn write_end_all(&mut self) {
    for ew in self.1.iter_mut().rev() {
        ew.write_end(self.0);
        // do on current el.write_end();
    }
}

它更简单,不会导致尝试实例化无数个函数,并且可能不是您正在寻找的功能:)