【问题标题】:Rust Lifetime issue when chaining local iterator in recursive function在递归函数中链接本地迭代器时出现 Rust Lifetime 问题
【发布时间】:2019-12-25 22:31:25
【问题描述】:

我有一个递归调用的通用函数,它可以接受可以转换为迭代器的参数。

我想将作为参数传递的迭代器与从函数本地集合创建的迭代器链接起来。这个想法是,在多次调用递归函数后,会创建一个迭代器,该迭代器能够迭代位于其各自堆栈帧中的所有集合。

fn func_taking_iter<'a, I>(vals: I) -> ()
where
    I: IntoIterator<Item = &'a u32>,
    I::IntoIter: Clone,
{
    let vals = vals.into_iter();
    let mut new_val: [u32; 1] = [0u32; 1];
    for x in vals.clone() {
        new_val[0] = *x;
    }

    new_val[0] += 1u32;
    if new_val[0] == 10u32 {
        for x in vals {
            println!("Value {}", *x);
        }
        return;
    }

    let res = vals.into_iter().chain(new_val.into_iter());

    func_taking_iter(res);
}

fn main() {
    let first_val = [0u32; 1];
    func_taking_iter(first_val.iter());
}

不幸的是,当我尝试编译代码时出现以下错误。

error[E0597]: `new_val` does not live long enough                                                 
  --> src\main.rs:20:38
   |
1  | fn func_taking_iter<'a, I>(vals: I) -> ()
   |                     -- lifetime `'a` defined here
...
20 |     let res = vals.into_iter().chain(new_val.into_iter());
   |               -----------------------^^^^^^^-------------
   |               |                      |
   |               |                      borrowed value does not live long enough
   |               argument requires that `new_val` is borrowed for `'a`
...
23 | }
   | - `new_val` dropped here while still borrowed

我在 Rust 方面没有太多经验,我被困在这里,但在我看来,这似乎不是一件不可能的事情......


编辑:改进了示例代码,由于 Stargateur 的 answer 中的提示,只有迭代器而不是整个集合被克隆

【问题讨论】:

    标签: recursion rust iterator lifetime chain


    【解决方案1】:

    您将新数组的生命周期绑定到函数之外的生命周期'a,这是生命周期系统的限制,我认为您今天无法修复它,也许将来它会编译。但是,即使您可以解决生命周期问题,您最终也会遇到“评估需求的溢出”(E0275])。编译器需要推断出无限多的函数,因为对这个函数的无限调用是无限链结构的。

    我不建议在 rust 中编写这种代码,链接这么多迭代器效率不高。我认为您应该更改代码以使用向量。这将节省您的堆栈空间,因为无论如何您的函数不是尾递归的,因此您可能会以堆栈溢出问题结束。

    fn func_taking_iter_aux(vals: Vec<u32>) -> Vec<u32> {
        let new_val = vals.last().copied().unwrap_or(0) + 1;
    
        if new_val == 10u32 {
            vals
        } else {
            let mut vals = vals;
            vals.push(new_val);
            func_taking_iter_aux(vals)
        }
    }
    
    fn func_taking_iter<'a, I>(vals: I) -> Vec<u32>
    where
        I: IntoIterator<Item = &'a u32>,
    {
        func_taking_iter_aux(vals.into_iter().copied().collect())
    }
    
    fn main() {
        let vals = func_taking_iter(Some(&0));
        for x in vals {
            println!("Value {}", x);
        }
    }
    

    我尽量满足您的要求。现在这是一个尾递归函数。


    请注意,vals.clone() 在您的代码中克隆所有数据而不仅仅是迭代器,您应该这样做 let vals = vals.into_iter() 然后您可以克隆迭代器,当然还可以删除 I: Clone,

    【讨论】:

    • 您能否更详细地解释在我的具体情况下导致“评估需求的溢出”(E0275])错误的原因?我在这种情况下理解它stackoverflow.com/questions/31196422/…,但我在这里看不到相同的方案。
    • @clockworkman 你在编译时使用了静态调度,所以真正的类型,所以通用I 在第一次调用(简化)Slice1 然后是Chain&lt;Slice1, Slice2&gt; 然后是Chain&lt;Chain&lt;Slice1, Slice2&gt;, Slice3&gt; 然后是@987654331 @ etc etc 没有尽头,我认为可以使用 dyn dispatch 解决,但这又是一个非常糟糕的主意
    猜你喜欢
    • 1970-01-01
    • 2020-05-24
    • 2020-07-05
    • 2011-04-17
    • 1970-01-01
    • 1970-01-01
    • 2019-12-23
    • 1970-01-01
    • 2017-11-29
    相关资源
    最近更新 更多