More lifetime issues :(


#1

So again I am struggling with lifetimes.
I have a basic example below

I have a pipeline object that contains two vectors. The first holds Element’s which is a trait and the second
ElementPads which are normal structs. I have added lifetimes on the Pipeline struct for both types. I also have two functions one to find a Element and one to find a ElementPad.
This throws the error

cannot infer an appropriate lifetime for lifetime parameter 'a due to conflicting requirements

at this line

let element : &Element = try!(self.find_element(name));

Why is this ?
the return on that function is

PipelineResult<(&'a Element, &'b ElementPad)>

so should the compiler be able to infer this ?

Thanks

pub type ElementResult<T> = Result<T, String>;

pub struct ElementPad {
    pub name : String,
}

pub trait Element {

    fn get_name(&self) -> String;
    fn get_pad(&self, name : &str) -> ElementResult<&ElementPad>;
}


pub type PipelineResult<T> = Result<T, String>;

pub struct Pipeline<'a, 'b> {
    elements : Vec<&'a Element>,
    element_pads : Vec<&'b ElementPad>,
}

impl<'a, 'b> Pipeline<'a, 'b> {

    fn find_element(&self, name : &str) -> PipelineResult<&'a Element> {

        let option = self.elements.iter().find(|&r| r.get_name() == name.to_string());

        match option {
            Some(e) => Ok(*e),
            None => {
             warn!("Element with name {} not found in pipeline", name);
             return Err("Error".to_string())   
            },
        }
    }

    // Comment this and things are fine
    fn find_element_pad(&self, name : &str, pad_name : &str) -> PipelineResult<(&'a Element, &'b ElementPad)> {

        let element : &Element = try!(self.find_element(name));
        let pad = try!(element.get_pad(pad_name));
        
        Ok((element, pad))
    }
}

#2

Ok, I tried this:

https://is.gd/FP9LCC

basically, I think you need this:

fn find_element_pad<'s>(&'s self, name : &'a str, pad_name : &str)
      -> PipelineResult<(&'a Element, &'b ElementPad)> 
      where 's: 'b

Because the lifetime of the actual Pipeline struct needs to be taken into consideration when returning it’s inner references. The 's: 'b bounds the s to live at least as long as b. In my playground example I also bounded 'b: 'a so that s lives longer than b lives longer than a…


#3

Thanks a lot.
Makes sense.

Out of curiosity was that just experience or did you infer something from the error I didn’t ?


#4

Experience mostly. The error is hard to grok from the report that the compiler gives. So what I do is sit back sometimes and try and understand what it is that is happening… so reading the code I basically was like, is there more information that I can give the compiler, and what is missing. In this context I noticed that (if I understood your code/intentions properly!) that 'b would always be derived from 'a and the the function find_element_pad() wasn’t declaring the actual struct’s lifetime itself, but it was implicitly getting one from the &self.

Honestly, I’ve never tried code like you put up before, so it was a fun challenge to see if I could figure it out!


#5

Sorry to bother you again but wonder as you said you have not tried code like this that its style is not very Rust like.

I updated vour example

https://play.rust-lang.org/?gist=a44d6fbd88ff23542a3021452f699092&version=stable&backtrace=0

It basically adds a mut method to insert into the vector and added a run method.

I just get errors like
"second mutable borrow occurs here" and mixing borrow errors that I usually get with self methods.
How are errors like this dealt with or does it suggest a problem with the design ?

Thanks


#6

So I have to ask a question at this point. Who do you plan on owning this data? I mean, you have a lot of borrowing going on, but it might be better if the Element had owned Strings, i.e. “string”.to_string()

Anyway, the crux of your current issue is that the caller because the &strs are borrowed, and those borrows cause the mutable borrow to live longer than the initial add call. It would be better if on insertion, it was a fully owned object, then you could borrow it back where necessary…

does that make sense?


#7

Yeah Thanks that does make sense.
I think you have to think more returning references from structs / objects.
In the end I have made ElementPad implement clone which seems reasonable as they are simple objects.
This seems to correct things and makes some of the other code easier to understand.

Understanding a little more each day :slight_smile:

Thanks