How can I extend the lifetime of a temporary variable inside of an iterator adaptor in Rust?

雨燕双飞 提交于 2021-01-29 07:51:09

问题


I have a method make_iter() which creates an Iterator with multiple adapters in Rust, which can be simplified as the following MCVE:

fn make_iter(first: &First) -> Box<dyn Iterator<Item = String> + '_> {
    Box::new(first.make_objects().flat_map(|second| {
        second
            .iter()
            .filter(|third| third.as_str() != "t2")
            .flat_map(|third| vec![format!("{}.A", third), format!("{}.B", third)].into_iter())
            .chain(
                vec![
                    format!("{}.A", second.name()),
                    format!("{}.B", second.name()),
                ]
                .into_iter(),
            )
    }))
}

pub fn main() {
    let first = First {};
    for i in make_iter(&first) {
        println!("{}", i);
    }
}

struct First {}

impl First {
    fn make_objects(&self) -> Box<dyn Iterator<Item = Second> + '_> {
        Box::new(
            vec![
                Second::new("s1".to_string()),
                Second::new("s2".to_string()),
                Second::new("s3".to_string()),
            ]
            .into_iter(),
        )
    }
}

struct Second {
    name: String,
    objects: Vec<String>,
}

impl Second {
    fn new(name: String) -> Second {
        Second {
            name,
            objects: vec!["t1".to_string(), "t2".to_string(), "t3".to_string()],
        }
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn iter(&self) -> Box<dyn Iterator<Item = &String> + '_> {
        Box::new(self.objects.iter())
    }
}

Trying to compile this example yields a lifetime error:

error[E0597]: `second` does not live long enough
  --> src/main.rs:3:9
   |
3  |         second
   |         ^^^^^^ borrowed value does not live long enough
...
14 |     }))
   |     - `second` dropped here while still borrowed

This is understandable, as the second object is a temporary, and the iterator returned from the same closure attempts to borrow it, failing as second is dropped at the closure's end. My objective would be to extend the lifetime of this object until the Iterator referencing it is dropped, but I don't know how.

Note that the structure implementations cannot be changed. Rust version is 1.34.2, edition 2018


回答1:


extend the lifetime of this object

You cannot do this, period. It's simply not how things work.

See also:

  • Extend lifetime of variable
  • Extending borrowed lifetime for String slice
  • Is there any way to return a reference to a variable created in a function?

Here's a simpler reproduction:

use std::iter;

fn example(outer: impl Iterator<Item = Inner>) {
    outer.flat_map(|i| i.iter().map(|v| v.clone()));
}

struct Inner;

impl Inner {
    fn iter(&self) -> impl Iterator<Item = &()> + '_ {
        iter::empty()
    }
}

Since you have the restriction of being unable to change Inner, the easiest solution is to be more eager and proactively collect:

fn example(outer: impl Iterator<Item = Inner>) {
    outer.flat_map(|i| i.iter().map(|v| v.clone()).collect::<Vec<_>>());
}

The only possibility I know of to avoid that would be to use a crate like rental or owning_ref.

See also:

  • Is there an owned version of String::chars?
  • How can I store a Chars iterator in the same struct as the String it is iterating on?

If you had the possibility of changing Inner, you should make a consuming iterator variant. Then the value does not need to live beyond the closure because the iterator has taken ownership.

use std::iter;

fn example(outer: impl Iterator<Item = Inner>) {
    outer.flat_map(|i| i.into_iter().map(|v| v));
}

struct Inner;

impl Inner {
    fn into_iter(self) -> impl Iterator<Item = ()> {
        iter::empty()
    }
}


来源:https://stackoverflow.com/questions/56691225/how-can-i-extend-the-lifetime-of-a-temporary-variable-inside-of-an-iterator-adap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!