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)
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));
}
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.