问题
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