Conflicting lifetime parameters: Trait vs. Struct


#1

I want a struct that holds a &str. I have a trait Parse that wants to parse a &str and return a T.

I’m now lost in a twisty maze of lifetimes, all unlike. The basic issue is that my parse method and my struct both want to hold on to declared lifetimes, and I can provide no guarantees to the complier that the lifetimes are compatible. At the same time, I can’t tell the complier that parse want to return a borrow that lasts as long as the struct.

pub trait Parse<T> {
    fn parse<'a>(v: &'a str) -> Result<T,()>;
}

pub struct Field<'a> {
    pub name: Fields,
    pub value: &'a str
}

impl<'a> Parse<Field<'a>> for Field<'a> {
    fn parse<'b>(v: &'b str) -> Result<Self,()> {
        let fields: Vec<&'b str> = v.split(":").collect();
        if fields.len() < 2 {
            Err(())
        } else {
            Ok(Field { name: Fields::from(fields[0]),
                       value: fields[1]})
        }
    }
}


lib.rs:66:35: 66:44 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
lib.rs:66                            value: fields[1]})
                                            ^~~~~~~~~
lib.rs:60:9: 68:10 help: consider using an explicit lifetime parameter as shown: fn parse(v: &'a str) -> Result<Self, MailError>

Note that the error message asks for an incorrect and invalid fix.

I’ve tried to cast the lifetime to 'a, but that gives me an outlives error.


#2

The trait method being generic over the lifetime means you can’t make any assumptions about that lifetime in the implementation. I guess you could make the trait have the parameter as well:

pub trait Parse<'a, T> {
    fn parse(v: &'a str) -> Result<T,()>;
}

pub struct Field<'a> {
    pub value: &'a str,
}

impl<'a> Parse<'a, Field<'a>> for Field<'a> {
    fn parse(v: &'a str) -> Result<Self,()> {
        Ok(Field { value: v })
    }
}

Also note that

fn parse<'a>(v: &'a str) -> Result<T,()>

is equivalent to

fn parse(v: &str) -> Result<T,()>

due to lifetime elision.


#3

What about http://is.gd/QP91HG ?


#4

Well golly – making the trait have a lifetime type parameter works. (Now I need to figure out why it works…)

Thanks!


#5

There needs to be a parameter denoting the lifetime of v bound to the lifetime of the struct somehow. A parameter on the method would be unbounded but a parameter on the trait allows us to require (Parse<'a, ...> for Field<'a>) that the lifetime of Field is the same as that of v.

Technically, we only need v to outlive Field, as in

pub trait Parse<'a, T> {
    fn parse(v: &'a str) -> Result<T,()>;
}

pub struct Field<'a> {
    pub value: &'a str,
}

impl<'a, 'b: 'a> Parse<'b, Field<'a>> for Field<'a> {
    fn parse(v: &'b str) -> Result<Self,()> {
        Ok(Field { value: v })
    }
}

This will allow using a string literal, which is a &'static str, to get a shorter lived Field.

T seems redundant here BTW, the trait would achieve the same with just the lifetime parameter and Result<Self, ()> return type.