Naming a lifetime local to a called function

As shown below, I would like to pass a closure Fn(T) to a function generic over T, which will deserialize a T from stack-local data, possibly sharing structure, and then call the closure on it. But when T is a borrow like &str, I can't work out how to name the lifetime. Thank you for any tips!

use serde::*;
use std::fmt::Debug;

// Obtain some input, deserialize it, and then
// call the given function on it.
fn map_deserialized<T, F>(f: F)
where
    T: for<'de> Deserialize<'de> + Debug,
    F: Fn(T),
{
    // Obtain some data and store it locally
    let data: String = r#"   "hello"   "#.to_owned();

    // Decode it to `x`, sharing where possible
    let x: T = serde_json::from_str(&data).unwrap();

    f(x);
}

pub fn main() {
    map_deserialized(|s: String| println!("string: {:?}", s));

    // map_deserialized(|s: &str| println!("str: {:?}", s));
    //
    // error: implementation of `serde::Deserialize` is not general enough
    //   note: `&str` must implement `serde::Deserialize<'0>`, for any lifetime `'0`...
    //   note: ...but `&str` actually implements `serde::Deserialize<'1>`, for some specific lifetime `'1`
}

// And this signature doesn't work at all:
//     error[E0597]: `data` does not live long enough
//
// fn map_deserialized_wrong<'de, T, F>(f: F)
// where
//     T: Deserialize<'de> + Debug,
//     F: Fn(T)

(Playground)

When there is a lifetime in the deserialized data, then there is not a single type T and so you cannot specify it using a type parameter. You have to use something more roundabout, like using a generic associated type, to identify the family of "types with one lifetime parameter" using a placeholder type:

use serde::*;
use std::fmt::Debug;

trait Proxy {
    type ActualType<'a>;
}
impl Proxy for String {
    type ActualType<'a> = String;
}
impl Proxy for &'static str {
    type ActualType<'a> = &'a str;
}

fn map_deserialized<T: Proxy, F>(f: F)
where
    for<'de> <T as Proxy>::ActualType<'de>: Deserialize<'de> + Debug,
    for<'de> F: Fn(<T as Proxy>::ActualType<'de>),
{
    let data: String = r#"   "hello"   "#.to_owned();
    let x: <T as Proxy>::ActualType<'_> = serde_json::from_str(&data).unwrap();
    f(x);
}

pub fn main() {
    map_deserialized::<String, _>(|s: String| println!("string: {:?}", s));

    map_deserialized::<&str, _>(|s: &str| println!("str: {:?}", s));
}
2 Likes

Aha, thank you very much! GATs had been floating around at the periphery of my consciousness but I hadn't understood what they were. You've helped me into a new stage of Rust learning.

I wasn't sure how I could provide these proxies for types outside my library, but I'm now realizing I may be able to present things to the compiler in a simpler way (with the help of macros), avoiding this challenging pattern.

Thank you again for the explanation and the patched example.

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.