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
Shorten serde-derive generated code #2310
Comments
Yes, I support this. I am writing a project, and it takes so long to compile. I don't know much about how this particular macro works. But if the derive macros for Deserialize implements all the type it supports, it would be very much time consuming. It could be implemented functionalities like:
And if you want to implement for all it should be like: So, perhaps it would be better to add option to implement Deserialize for json or our specific use cases only. |
I don't think this issue contains actionable information, because in general the length of the generated code is not usually the issue. Above, you showed 165 lines of serde_derive-generated code to illustrate how it's verbose, but there are good reasons for macro-generated code to be verbose relative to handwritten code (hygiene) and some of those reasons improve compile time, in contrast to what one might expect by focusing on length of code (less reliance on name resolution, less reliance on type conversions, fewer type inference variables, less use of generic closure-based APIs, fewer function calls, fewer autoref or autoderef operations, ...). To illustrate this, here is a 100% equivalent impl in handwritten style in 57 lines (⅓ as many) that takes longer to compile. impl<'de> serde::Deserialize<'de> for Foo {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Field(u64);
struct FieldVisitor;
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("field identifier")
}
fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Field, E> {
Ok(Field(v))
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Field, E> {
self.visit_bytes(v.as_bytes())
}
fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Field, E> {
Ok(Field(if v == b"foo" { 0 } else { 1 }))
}
}
impl<'de> serde::Deserialize<'de> for Field {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct FooVisitor;
impl<'de> serde::de::Visitor<'de> for FooVisitor {
type Value = Foo;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct Foo")
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Foo, A::Error> {
let foo = seq.next_element()?.ok_or_else(|| {
serde::de::Error::invalid_length(0, &"struct Foo with 1 element")
})?;
Ok(Foo { foo })
}
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Foo, A::Error> {
let mut foo = None;
while let Some(field) = map.next_key()? {
if let Field(0) = field {
if foo.is_some() {
return Err(serde::de::Error::duplicate_field("foo"));
}
foo = map.next_value().map(Some)?;
} else {
map.next_value::<serde::de::IgnoredAny>()?;
}
}
if foo.is_none() {
foo = serde::__private::de::missing_field("foo").map(Some)?;
}
Ok(Foo { foo: foo.unwrap() })
}
}
deserializer.deserialize_struct("Foo", &["foo"], FooVisitor)
}
} |
In the serenity community, we made some quick tests and noticed that derived Serialize/Deserialize implementations make up ~75% of base compile time. I think there is improvement potential.
A simple struct:
Generates a mountain of code:
Playground link (run Tools -> Expand Macros)
It feels like this can be reduced a lot, perhaps by moving repetitive code into #[doc(hidden)] public functions in serde, which the derive-generated code calls into.
Related: #1146 (comment), #286
The text was updated successfully, but these errors were encountered: