Lifetime issue: trait with lifetime, generics, oh my

I have a function simplified as follows:

fn validate_message<V>(data: String) -> anyhow::Result<()>
where
    V: for<'a> Deserialize<'a>,
{
    for line in data.lines() {
        if serde_json::from_str::<V>(line).is_err() {
            return Err(anyhow!("boo"));
        }
    }
    Ok(())
}

This function fails to compile:

error[E0597]: `data` does not live long enough
  --> src/main.rs:22:17
   |
18 | fn validate_message<'a, V>(data: String) -> anyhow::Result<()>
   |                     --     ---- binding `data` declared here
   |                     |
   |                     lifetime `'a` defined here
...
22 |     for line in data.lines() {
   |                 ^^^^ borrowed value does not live long enough
23 |         if serde_json::from_str::<V>(line).is_err() {
   |            ------------------------------- argument requires that `data` is borrowed for `'a`
...
28 | }
   | - `data` dropped here while still borrowed

The issue is, while the trait the function is generic over contains a lifetime, in practice the lifetime does not escape the function, and is based on allocation performed within the function.
How do I indicate it to Rust though?

Executable example:

(While passing the data as reference would work in the simplified case, in the real life case I also stream decompress the data, so this workaround is not available).

(Your first code block with the for<'a> doesn't match your error block.)

You need a type parameter that can represents One<'a> for a lifetime 'a too short for the caller to name. The HRTB (for<'a>) doesn't work because a type parameter must also resolve to a single concrete type, whereas two One<'_> that differ by lifetimes are distinct types -- so no One<'a> satisfies the higher-ranked bound.

So you really want a type parameter that represents the ability to construct a concrete type from an arbitrary lifetime (like the local borrows too short for the caller to name):

// A trait that represents the type constructor
trait DeserializeGat {
    // With the desired functionality on the constructed types
    type Out<'a>: Deserialize<'a>;
}

// A single type that represents all `One<'_>`
struct OneMarker;
impl DeserializeGat for OneMarker {
    type Out<'a> = One<'a>;
}

// Changed bound    vvvvvvvvvvvvvvvvv
fn validate_message<V: DeserializeGat>(data: String) -> anyhow::Result<()> {
    for line in data.lines() {
        // Making use of the type constructor
        //                        vvvvvvvvvv
        if serde_json::from_str::<V::Out<'_>>(line).is_err() {
            return Err(anyhow!("boo"));
        }
    }
    Ok(())
}

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.