1

I'm trying to rewrite a method to use existential types, and I am having trouble deciphering the error:

use std::result;

pub struct Child {
    pub value: u32,
}

pub struct Parent {
    pub name: u32,
}

impl Parent {
    pub fn process(&self, _l: &Child) -> result::Result<(), ()> {
        Ok(())
    }

    pub fn convert(&self, l: &Child) {
        ()
    }

    pub fn looper(&self, l: Vec<Child>) -> impl Iterator<Item = Result<Child, ()>> {
        let x: Vec<_> = l.into_iter()
            .map(|tr| self.process(&tr).map(|_| tr))
            .collect(); // Calling collect() here forces all debits to complete

        let y = x.into_iter().map(|d| {
            d.map(|c| {
                self.convert(&c);
                c
            })
        });
        y
    }
}

fn main() {
    let b = Parent { name: 0 };
    let l = vec![Child { value: 10 }, Child { value: 20 }];
    let _: Vec<Child> = b.looper(l).map(|x| x.unwrap()).collect();
}

My error message states:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:25:35
   |
25 |           let y = x.into_iter().map(|d| {
   |  ___________________________________^
26 | |             d.map(|c| {
27 | |                 self.convert(&c);
28 | |                 c
29 | |             })
30 | |         });
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 20:5...
  --> src/main.rs:20:5
   |
20 | /     pub fn looper(&self, l: Vec<Child>) -> impl Iterator<Item = Result<Child, ()>> {
21 | |         let x: Vec<_> = l.into_iter()
22 | |             .map(|tr| self.process(&tr).map(|_| tr))
23 | |             .collect(); // Calling collect() here forces all debits to complete
...  |
31 | |         y
32 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &&Parent
              found &&Parent
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that return value is valid for the call
  --> src/main.rs:20:44
   |
20 |     pub fn looper(&self, l: Vec<Child>) -> impl Iterator<Item = Result<Child, ()>> {
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Rob
  • 3,333
  • 5
  • 28
  • 71
  • 1
    you are borrowing self in the closure, https://play.rust-lang.org/?gist=8a33f614f7fec008097ce163459f6e86&version=stable&mode=debug – Stargateur May 15 '18 at 19:08
  • I believe your question is answered by the answers of [Lifetimes for method returning iterator of structs with same lifetime](https://stackoverflow.com/q/50343130/155423). TL;DR: `pub fn looper<'a>(&'a self, l: Vec) -> impl Iterator> + 'a` and add `move` to your closures. If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster May 15 '18 at 19:10

1 Answers1

3
let y = x.into_iter().map(|d| {
    d.map(|c| {
        self.convert(&c); // <--- This
         c
    })
});

This borrows &self in the returned iterator, because every time convert is called, it requires that an instance of &self is passed to the function (even if it is unused).

To explicitly make the lifetime correct, the syntax you want is:

fn foo<'a>(/* ... */) -> impl 'a + Trait

e.g.

pub fn looper<'a>(&'a self, l: /* ... */) -> impl 'a + Iterator<Item = Result<Child, ()>> { /* ... */ }

i.e. Return an impl Iterator<Item = Result<Child, ()>> with the same lifetime as the borrowed object.

The default is that impl Foo returns a 'static lifetime instance, which isn't valid in this case, because convert borrows &self.

Like this:

use std::result;

pub struct Child {
    pub value: u32,
}

pub struct Parent {
    pub name: u32,
}

impl Parent {
    pub fn process(&self, _l: &Child) -> result::Result<(), ()> {
        Ok(())
    }

    pub fn convert(&self, l: &Child) {
        ()
    }

    pub fn looper<'a>(&'a self, l: Vec<Child>) -> impl 'a + Iterator<Item = Result<Child, ()>> {
        let x: Vec<_> = l.into_iter()
            .map(|tr| self.process(&tr).map(|_| tr))
            .collect(); // Calling collect() here forces all debits to complete

        let y = x.into_iter().map(move |d| {
            d.map(|c| {
                self.convert(&c);
                c
            })
        });
        y
    }
}

fn main() {
    let b = Parent { name: 0 };
    let l = vec![Child { value: 10 }, Child { value: 20 }];
    let k = b.looper(l);
    drop(b); // <-- Doesn't work.
    let _: Vec<Child> = k.map(|x| x.unwrap()).collect();
}

Notice the compiler correctly complains about attempting to throw away b while the iterator is not resolved:

error[E0505]: cannot move out of `b` because it is borrowed
  --> src/main.rs:39:10
   |
38 |     let k = b.looper(l);
   |             - borrow of `b` occurs here
39 |     drop(b);
   |          ^ move out of `b` occurs here

Alternatively, you could just remove the reference to &self from convert:

pub fn convert(l: &Child) {
    ()
}
Doug
  • 32,844
  • 38
  • 166
  • 222
  • Why do you disagree that this is a duplicate? – Shepmaster May 16 '18 at 01:58
  • @Shepmaster this question is literally 'what is the syntax for adding a lifetime to an impl Foo'; that question is 'how do I write an iterator that returns objects with references in it?`; the answer may be similar, but the questions are not the same. – Doug May 16 '18 at 02:04
  • As surprising as it is to many users, Stack Overflow duplicates aren't about questions, they are about *answers*. If the question is answered, they are duplicates. – Shepmaster May 16 '18 at 02:06
  • I'd argue with "is literally" as well. OP asks not for syntax but to solve their problem. They didn't ask how to specify anything. – Shepmaster May 16 '18 at 02:08
  • /shrug I have no idea 'whats best', I was just trying to help the OP instead of posting a comment, because getting a comment on your question instead of an actual answer is pretty frustrating, from personal experience. If answering the OP's question is someone the *wrong thing* to have done on a *Q&A* site, then I'm sorry. You're the mod with 100+K rep; do whatever it is that SO mods do in this kind of situation. – Doug May 16 '18 at 02:12
  • 1
    I'm not a mod, mods have a diamond next to their names; I have almost exactly the same powers you do (modulo my [tag:rust] gold badge and some [minor editing rights](/help/privileges)). When we mark as duplicate, we try to provide a comment connecting the duplicate to the OPs specific case. The reason for encouraging duplicates is to avoid scattering different shards of 70% of the answer across multiple posts, which ultimately makes it harder for people to find an answer. The ultimate goal of SO is to prevent ever needing to ask a question again because they've all already been answered. – Shepmaster May 16 '18 at 02:20
  • Fair enough. I feel like if I was the OP, I would look at the linked question and go 'but I'm not trying to return &self.foo in my iterator, Child is a concrete type with no references in it, this isn't the same question, I don't see how this is helpful'. – Doug May 16 '18 at 02:23
  • That's kind of what I was getting at with my first comment (and why I try not to close questions though I can) — I want to encourage new and unique questions, shaping them through iteration if need be. Focusing the question (by comments and by editing), drawing distinctions between existing questions, etc., are all good and useful. We asked the OP to provide that 7 hours ago and they were last seen 5 hours ago; I don't know why they didn't respond then. Usually we we wait a day before closing it in those cases. – Shepmaster May 16 '18 at 02:29
  • Doug's answer helped me solve my problem, as did your comment. I also am not sure what the appropriate measure is to take, I felt Doug's response helped me better understand the question because it provided an alternate explanation, and I value the effort he put into this. I think Rust is not too saturated with questions, but appreciate your commitment to policing the Rust SO shepmaster. – Rob May 16 '18 at 03:21