Wrong `'static` bound on derived `Deserialize`
I've been trying to deserialize a file into a Tree, with its contents leaked to be static.
#[derive(Deserialize)]
struct Tree {
#[serde(deserialize_with = "deserialize_leaked")]
node: &'static str,
#[serde(deserialize_with = "deserialize_leaked")]
children: &'static [Tree],
}
fn deserialize_leaked<'de, D, T>(d: D) -> Result<&'static T, D::Error>
where
T: ?Sized,
Box<T>: Deserialize<'de>,
D: Deserializer<'de>,
{
Box::<T>::deserialize(d).map(|b| Box::leak(b) as &'static _)
}
This works fine in principle, but the macro code then generates a
impl Deserialize<'static> for Tree { ... }
but I need it to be
impl<'de> Deserialize<'de> for Tree { ... }
I have found a workaround by using a type alias
type StaticStr = &'static str;
#[derive(Deserialize)]
struct Tree {
#[serde(deserialize_with = "deserialize_leaked")]
node: StaticStr,
#[serde(deserialize_with = "deserialize_leaked")]
children: &'static [Tree],
}
fn deserialize_leaked<'de, D, T>(d: D) -> Result<&'static T, D::Error>
where
T: ?Sized,
Box<T>: Deserialize<'de>,
D: Deserializer<'de>,
{
Box::<T>::deserialize(d).map(|b| Box::leak(b) as &'static _)
}
which has the expected behavior, but it's not very nice.
While browsing the the internet, I found the borrow attribute, but unfortunately this doesn't allow me to specify zero lifetime arguments. While that seems like a reasonable restriction, in this case that is precisely what I want:
#[derive(Deserialize)]
struct Tree {
#[serde(borrow="", deserialize_with = "deserialize_leaked")]
node: &'static str,
#[serde(deserialize_with = "deserialize_leaked")]
children: &'static [Tree],
}
fn deserialize_leaked<'de, D, T>(d: D) -> Result<&'static T, D::Error>
where
T: ?Sized,
Box<T>: Deserialize<'de>,
D: Deserializer<'de>,
{
Box::<T>::deserialize(d).map(|b| Box::leak(b) as &'static _)
}
Though it's also interesting, why is the type alias only required on the &'static str but not the &'static [Tree]?
I wanted to simply suggest to allow #[serde(borrow = ""), but something else feels off here.
serde(bound = ""), either on the node field or on the whole struct, should do the trick. That supersedes all built-in inference of trait bounds and lifetime bounds.
There is a difference between &'static str and &'static [Tree] due to the first sentence in https://serde.rs/lifetimes.html#borrowing-data-in-a-derived-impl: only &'a str and &'a [u8] result in 'de: 'a lifetime bound implicitly being added by serde to the derived impl. Other field types add no lifetime bound implicitly and one must use a borrow or bound serde attribute to get a lifetime bound.
Thank you for the explanation. Using serde(bound = "") does indeed work.
I suppose if more complex (generic) types don't have this inferred borrow anyway, then this is equivalent to the serde(borrow="").
Though I'd think the borrow attribute would be more intuitive.
Feel free to close this if you think using bound is good enough!