Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#[serde(flatten)] on map types doesn't use SerializeMap::serialize_entry #1837

Closed
Schuwi opened this issue Jun 14, 2020 · 0 comments · Fixed by #1839
Closed

#[serde(flatten)] on map types doesn't use SerializeMap::serialize_entry #1837

Schuwi opened this issue Jun 14, 2020 · 0 comments · Fixed by #1839

Comments

@Schuwi
Copy link

Schuwi commented Jun 14, 2020

When annotating a map type (e.g. HashMap) field with #[serde(flatten)] the generated code ultimately calls Serializer::collect_map which in turn calls FlatMapSerializeMap::serialize_entry.

serde/serde/src/ser/mod.rs

Lines 1314 to 1326 in 95b1a5d

fn collect_map<K, V, I>(self, iter: I) -> Result<Self::Ok, Self::Error>
where
K: Serialize,
V: Serialize,
I: IntoIterator<Item = (K, V)>,
{
let iter = iter.into_iter();
let mut serializer = try!(self.serialize_map(iter.len_hint()));
for (key, value) in iter {
try!(serializer.serialize_entry(&key, &value));
}
serializer.end()
}

As this function is not implemented on FlatMapSerializeMap the default SerializeMap::serialize_entry implementation delegates this to calls of FlatMapSerializeMap::serialize_key and FlatMapSerializeMap::serialize_value.

serde/serde/src/private/ser.rs

Lines 1224 to 1249 in 95b1a5d

#[cfg(any(feature = "std", feature = "alloc"))]
impl<'a, M> ser::SerializeMap for FlatMapSerializeMap<'a, M>
where
M: SerializeMap + 'a,
{
type Ok = ();
type Error = M::Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.0.serialize_key(key)
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.0.serialize_value(value)
}
fn end(self) -> Result<(), Self::Error> {
Ok(())
}
}

This is a problem for SerializeMap implementations which only implement the serialize_entry function (e.g. because the serialized key depends on the value type, as is the case in https://github.com/PistonDevelopers/hematite_nbt where in NBT the value type tag is written to binary before the key is).

A "minimal" playground implementation:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5bcbacafdd6927e0f668b71b0d9f6968

I would propose to implement the FlatMapSerializeMap::serialize_entry function as follows:

fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error>
where
    K: ?Sized + Serialize,
    V: ?Sized + Serialize,
{
    self.0.serialize_entry(key, value)
}

As every SerializeMap implementation that implements serialize_key and serialize_value has a default implementation for serialize_entry there shouldn't be any downsides to implementing this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

1 participant