Lifetime issue with `TryFrom`

The following code gives an error 's' does not live long enough:

use std::fmt::Debug;

pub enum PubType {
    Print,
    Online,
}

impl TryFrom<&str> for PubType {
    type Error = ();

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value {
            "print" => Ok(PubType::Print),
            "online" => Ok(PubType::Online),
            _ => Err(()),
        }
    }
}

fn f<'a, A>() -> Option<A>
where
    A: TryFrom<&'a str>,
    <A as TryFrom<&'a str>>::Error: Debug,
{
    // get value
    let value: Option<String> = Some("print".to_owned());

    // parse value
    value.map(|s| A::try_from(&s).unwrap())
}

fn main() {
    let _x: Option<PubType> = f();
}

Playground

Any idea how this can be fixed?

HRTBs to the rescue. The problem here is that you create the string slice A inside of f. Therefore you, f, the callee decides what the lifetime 'a is (some unnamable lifetime that exists inside of your function). But if you have a generic lifetime parameter 'a on f, the caller (i.e. in this case your main function) must decide what 'a is supposed to be. Because we don't allow main to specify 'a, we violate the contract of a generic (lifetime) parameter. The solution is to not expose 'a that way and instead have it as a higher ranked trait bound on A, which means that A implements TryFrom<&'a str> for any lifetime 'a, including the unnamable one that exists inside of f.

use std::fmt::Debug;

pub enum PubType {
    Print,
    Online,
}

impl TryFrom<&str> for PubType {
    type Error = ();

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value {
            "print" => Ok(PubType::Print),
            "online" => Ok(PubType::Online),
            _ => Err(()),
        }
    }
}

fn f<A>() -> Option<A>
where
    for<'a> A: TryFrom<&'a str>,
    for<'a> <A as TryFrom<&'a str>>::Error: Debug,
{
    // get value
    let value: Option<String> = Some("foo".to_owned());

    // parse value
    value.map(|s| A::try_from(&s).expect("get_try_from_opt"))
}

fn main() {
    let _x: Option<PubType> = f();
}

Playground.

6 Likes

Thanks for the great explantation, that makes perfect sense now :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.