3

I've got code that looks (a little) like this:

struct OutputIterator<'r, 'i: 'r> {
    input_handler: &'r mut InputHandler<'i>
}

impl<'r, 'i> Iterator for OutputIterator<'r, 'i> {
    type Item = i32;

    fn next(&mut self) -> Option<Self::Item> {
        self.input_handler.inputs.next().map(|x| x * 2)
    }
}

struct InputHandler<'a> {
    inputs: Box<dyn Iterator<Item = i32> + 'a>
}

impl<'a> InputHandler<'a> {
    fn outputs<'b>(&'b mut self) -> OutputIterator<'b, 'a> {
        OutputIterator { input_handler: self }
    }
}

fn main() {
    let mut input_handler = InputHandler {
        inputs: Box::new(vec![1,2,3,4,5].into_iter())
    };
    for output in input_handler.outputs() {
        println!("{}", output);
    }
}

Basically, the user can supply an iterator of inputs, and then get an iterator of outputs (in my real code the connection between inputs and outputs is much more complex involving a bunch of internal state. Multiple inputs might go towards one output or vice-versa).

This works, but I would like to change it use impl Iterator both to hide the OutputIterator type and to allow for easier swapping out of the return type in testing with a fake. My best attempt at that changes the InputHandler impl like so:

impl<'a> InputHandler<'a> {
    fn outputs<'b>(&'b mut self) -> impl Iterator<Item = i32> + 'b {
        OutputIterator { input_handler: self }
    }
}

Unfortunately, that gets me: error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds

Is there a way to make this work? It's important for the interface that InputHandler take an iterator with some lifetime, and that obviously has to be passed on to the OutputIterator somehow, but I'd really like to abstract those details away from the caller. In principle, the caller should only have to worry about making sure that the inputs Iterator and InputHandler outlive the OutputIterator so I think the logical lifetime bound on the OutputIterator here is the smaller of those two? Any clarity on why this error is happening or how to fix it would be great!

In case it helps, here's a rust playground with the code in it.

1 Answer 1

3

Using a workaround from https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999, via https://stackoverflow.com/a/50548538/1757964:

trait Captures<'a> {}
impl<'a, T: ?Sized> Captures<'a> for T {}

impl<'a> InputHandler<'a> {
    fn outputs<'b>(&'b mut self) -> impl Iterator<Item = i32> + Captures<'a> + 'b {
        OutputIterator { input_handler: self }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This works. Thanks! It's a little ugly of course, but it works! Do you have a sense if this is a necessary feature of how the lifetime system works, vs. being a rough edge they might fix some day?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.