How to return an iterator which cannot outlive a local variable

If I already have a function which could generate an iterator and that function is a blackbox for us.
fn iter<'a>(&'a context) -> impl Iterator+'a
Now we if we have another function, in which we have a local variable context.

fn get_iter() -> impl Iterator {
    context=...;
    // iter(&context)
}

Obviously we cannot return the iter(&context), because the iterator cannot outlive the context, which will not outlive current function.
The only way is to return the iterator with context together.
I reckon that the returned type should be like:

pub struct Context<B, I: Iterator> {
    context: B,
    iter: I,
}

The Context implement Iterator and just forward every associated functions like next() to iter.
In that schema, the question is transformed to how to construct Context.
More specifically, how to implement this function:

fn iter_with_context<B, F, I>(context: B, f: F) -> Context<B, I>
where
    I: Iterator,
    F: FnOnce(&B) -> I,
{
    unimplemented!()
}

Then several more problem emerge:
F: FnOnce(&B) -> I in the constrains acctually doesn't make sense, because returned type I is dependent on lifetime of &B. How to change it?
There are self referencing in the Context, how can we construct it?

Self referential types are very difficult to do with Rust, because if you move the container, the references inside the Iterator are not updated, invalidating the references.

If necessary, you can use Pin to create a self referential type, but this would make the type unable to be moved or require the use of Box to store the shared data somewhere that doesn't move.

It's not safe to have:

pub struct Context<B, I: Iterator> {
    context: B,
    iter: I/*which references context*/,
}

because move (such as returning from a function) of Context changes the address of the .context field, so any reference (such as a reference to the next element) in iter will be invalidated. It's not a theoretical problem, but an actual crash waiting to happen.

If you returned Box<Context> or had context: Box<B>, then the address wouldn't easily change, but in such cases the borrow checker is unable to prevent unsafe actions, such as reassignment of the .context field or mem::swap on it.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.