I can't get the following code to work (playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4379c2006dcf3d32f59b0e44626ca667).
use serde::{Serialize, Deserialize};
trait InnerStruct<'delife>: Deserialize<'delife> + Serialize {}
#[derive(Serialize, Deserialize)]
struct InnerStructA{
a: i32
}
impl InnerStruct<'_> for InnerStructA {}
#[derive(Serialize, Deserialize)]
struct InnerStructB{
a: i32,
b: i32
}
impl InnerStruct<'_> for InnerStructB {}
#[derive(Serialize, Deserialize)]
struct OuterStruct<T: InnerStruct>{ // Remove the word "InnerStruct" and this works
c: f64,
inner: T
}
fn print_json<T: for<'a> InnerStruct<'a>>(obj: T) {
println!("Serde JSON: {:?}", serde_json::to_string(&obj).unwrap());
}
fn main() {
let inner_a = InnerStructA{a: 123};
let inner_b = InnerStructB{a: 234, b: 567};
println!("Serde JSON: {:?}", serde_json::to_string(&inner_a).unwrap());
println!("Serde JSON: {:?}", serde_json::to_string(&inner_b).unwrap());
print_json(inner_a);
print_json(inner_b);
}
I have a collection of structs that are serializable (InnerStructA, InnerStructA), and they all implement a trait. Some functions are generic across a trait that unified them (InnerStruct). Some of these functions require that they are serializable and deserializable, so I've added Deserialize and Serialize supertraits to the trait definition. Deserialize required adding a named lifetime.
I now want an OuterStruct that is a generic container that could hold any type of inner struct. It works fine if I don't apply any trait bounds, but when I try to apply a trait bound to say this struct is only valid for T being InnerStruct everything breaks. The compiler messages talk about lifetimes, but none of the suggestions work.
A concrete example: For
struct OuterStruct<T: InnerStruct> {
the compiler suggests to
help: consider introducing a named lifetime parameter
|
23 | struct OuterStruct<'a, T: InnerStruct<'a>> {
but doing so leads to another error
error[E0392]: parameter `'a` is never used
--> src/main.rs:23:20
|
23 | struct OuterStruct<'a, T: InnerStruct<'a>> {
| ^^ unused parameter
What am I doing wrong?
Edit: DeserializeOwned If the trait is changed to DeserializedOwned then the lifetime issues go away, but the problem remains. It appears to be something to do with applying the derive(Deserialize) to the OuterStruct which already contains something that has had derive(Deserialize) applied to it. The error message is:
note: multiple `impl`s or `where` clauses satisfying `T: Deserialize<'_>` found
It's usually a good idea not to put any unnecessary bounds on a struct or enum. It's more flexible that way, especially when dealing with traits that have lifetime parameters.
So I would try something like this:
#[derive(Serialize, Deserialize)]
struct OuterStruct<T> {
c: f64,
inner: T,
}
fn print_json<'a, T>(obj: T)
where
T: InnerStruct<'a>,
{
println!("Serde JSON: {:?}", serde_json::to_string(&obj).unwrap());
}
In your example program, this will also work:
trait InnerStruct: DeserializeOwned + Serialize {}
...
#[derive(Serialize, Deserialize)]
struct OuterStruct<T: InnerStruct> {
c: f64,
#[serde(bound(deserialize = ""))]
inner: T,
}
Your original code wasn't working because you had a bound on the struct; and the #[derive(Deserialize)]
macro was copying that bound onto its impl<'a> Deserialize<'a> for
the struct, but also adding a T: Deserialize<'a>
bound. Usually that T:
bound is necessary, but in this case I guess Rust didn't like seeing T: Deserialize
required in two different ways. So the solution was to tell the macro not to emit its usual bound on T
.
The serde(bound)
attribute is documented here.