-
-
Notifications
You must be signed in to change notification settings - Fork 774
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
Introduce collect_seq and collect_map #736
Conversation
@sfackler on IRC taught me that there is a wrapper behind the feature |
Silly me, it uses If we think the wrapper is useful, we could keep it but simplify it using |
If we were going to do this it'd probably be worth also having one that serializes an iterator of tuples as a map. |
@sfackler Should I rename this one to |
3c33c5d
to
110e799
Compare
Can you share some example code of how you imagine people using this? As I see it, there are two primary use cases for these functions:
|
Same way as the unstable As for the fact that it is just a 5 lines loop, the IMO this is core enough that it should be in |
Sorry for the silence on my end - I have been busy with #739. I will consider this again tomorrow, including your suggestion from IRC of possibly making these Serializer trait methods with default implementations. |
I like the default method approach personally. It's a nice convenience similar to https://doc.rust-lang.org/std/fmt/struct.DebugList.html#method.entries |
It's a little different in that case because DebugList is a struct and not a trait. But sure, let's do it. Any thoughts on naming? Serializer::serialize_iter_as_seq and serialize_iter_as_map aren't perfect. It would be nice to reference |
Rather than |
Good idea, let's go with that. Thanks! |
Also I would prefer not to make the Pair changes. It makes sense for MapDeserializer because people pass around variables of type MapDeserializer<I, E> so additional redundant type parameters are a nuisance, but here let's just use K, V type parameters on collect_map. |
56f3805
to
8f7e8c4
Compare
Do you expect any serializers to want to override these default implementations? If so, please add a comment that explains why someone would want to override. If not, please add a comment that serializers should not need to override. I don't want serializer implementors to wonder whether the default implementation is doing the wrong thing for their format. |
|
@dtolnay I added the comments, I don't expect implementors to have to override this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. @oli-obk what do you think?
/// The default implementation serializes each item yielded by the iterator | ||
/// using `Self::SerializeSeq`. Implementors should not need to override | ||
/// this method. | ||
fn collect_seq<I>(self, iter: I) -> Result<Self::Ok, Self::Error> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can immediately use this in https://github.com/nox/serde/blob/a0a4d782957d351b608d08aa892e7e86317b5182/serde/src/ser/impls.rs#L224
Can we use the same size-trick so we don't pass None
?
Is there a way to use specialization to improve this impl for ExactSizeIterator
? If so, can this function be written in a way so that is possible (by adding a feature=unstable
specialization, which gets checked on nightly travis)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you even trust that size trick? I don't understand what tells you that this is actually correct to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size_hint() is primarily intended to be used for optimizations such as reserving space for the elements of the iterator, but must not be trusted to e.g. omit bounds checks in unsafe code. An incorrect implementation of size_hint() should not lead to memory safety violations.
Quoting the standard, since we don't guarantee that the Option
in serialize_seq
is correct, we can depend on the size_hint
to be just as correct.
All correct Serialize
impls will benefit from this, by being compatible with formats like bincode
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then I don't understand the second question, if we can just check that the two hints are equal and use that, why specialise for ExactSizeIterator
at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uhm... because I confused myself? ;) ignore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oli-obk I wonder if there is something to simplify around serialize_seq!
in ser::impls
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well... we could replace it with collect_seq
, but I wonder if that optimizes the same way. Without benchmarks that's not a change we should do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I introduced a trait LenHint
that is specialised for I: ExactSizeIterator
.
with your changes there's no need for it. Feel free to remove it in this PR. (it was behind an unstable feature flag anyway) |
This function serializes the given iterator as a sequence. Its iter parameter has type I: IntoIterator, <I as IntoIterator>::Item: Serialize, which means it will work both for iterators passed by value, therefore consuming them, and as the value for a #[serde(serialize_with)] attribute, where it will be called as Serializer::collect_seq(&self.field, serializer), relying on the common practice of implementing IntoIterator for &C where C is a data type representing some kind of collection.
@dtolnay: you wanna take another look? |
There is no difference in performance based on my benchmarks so I removed the old serialize_seq and serialize_map macros in 219abd2. |
These are two convenience functions to serialize iterators.