问题
I'm given a data-format that includes a sequence of objects with exactly one named field value
each. Can I remove this layer of indirection while deserializing?
When deserializing, the natural representation would be
/// Each record has it's own `{ value: ... }` object
#[derive(serde::Deserialize)]
struct Foobar<T> {
value: T,
}
/// The naive representation, via `Foobar`...
#[derive(serde::Deserialize)]
struct FoobarContainer {
values: Vec<Foobar<T>>,
}
While Foobar
adds no extra cost beyond T
, I'd like to remove this layer of indirection at the type-level:
#[derive(serde::Deserialize)]
struct FoobarContainer {
values: Vec<T>,
}
Can Foobar
be removed from FoobarContainer
, while still using it using deserialization?
回答1:
In the general case, there's no trivial way to make this transformation. For that, review these existing answers:
- How do I write a Serde Visitor to convert an array of arrays of strings to a Vec<Vec<f64>>?
- How to transform fields during deserialization using Serde?
The first is my normal go-to solution and looks like this in this example.
However, in your specific case, you say:
objects with exactly one named field value
And you've identified a key requirement:
While
Foobar
adds no extra cost beyondT
This means that you can make Foobar
have a transparent representation and use unsafe Rust to transmute between the types (although not actually with mem::transmute):
struct FoobarContainer<T> {
values: Vec<T>,
}
#[derive(serde::Deserialize)]
#[repr(transparent)]
struct Foobar<T> {
value: T,
}
impl<'de, T> serde::Deserialize<'de> for FoobarContainer<T>
where
T: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut v: Vec<Foobar<T>> = serde::Deserialize::deserialize(deserializer)?;
// I copied this from Stack Overflow without reading the surrounding
// text that describes why this is actually safe.
let values = unsafe {
let data = v.as_mut_ptr() as *mut T;
let len = v.len();
let cap = v.capacity();
std::mem::forget(v);
Vec::from_raw_parts(data, len, cap)
};
Ok(FoobarContainer { values })
}
}
See also:
- How do I convert a Vec<T> to a Vec<U> without copying the vector?
来源:https://stackoverflow.com/questions/58492598/deserialize-a-vecfoobart-as-vect-directly-when-foobar-has-exactly-one-fiel