'Do iterators define semantics for what `next()` should return after an error?
The basic question is: if an implementation of the Iterator trait returns a Result<T, E>, what should the iterator do after an error is returned from next() that makes it impossible to continue iterating?
Some context:
As a learning project, I'm attempting to implement a parsing library to parse STUN messages, as defined in RFC5389 in Rust. STUN is a binary network protocol, and as such I would be parsing byte slices. Specifically, the protocol specifies that zero or more dynamically-sized attributes can be encoded into the message. Thus, I am trying to construct an iterator which can be used to iterate over the bytes, yielding subslices for each attribute.
Thus, I have something like this...
pub struct StunAttributeIterator<'a> {
data: &'a [u8],
}
impl<'a> Iterator for StunAttributeIterator<'a> {
type Item = Result<StunAttribute<'a>, StunAttributeError>;
fn next(&mut self) -> Option<Self::Item> {
if self.data.len() == 0 {
return None;
}
// Ensure there are enough bytes left in the stream to parse the next attribute.
if self.data.len() < ATTRIBUTE_TYPE_LENGTH_BYTES {
return Some(Err(StunAttributeError::UnexpectedEndOfStream));
}
// Parse the data and get a slice of the dynamic bytes to return
let data = ...;
// Modify the iterator to have the slice move to the start of the next attribute
self.data = ...;
return Some(Ok(StunAttribute { data }));
}
}
There are a number of things that could go wrong here, and I've included an example in the if statement. If something goes wrong, it's a good indication that the byte stream being parsed is malformed, and thus there is no reason to continue attempting to parse.
On the one hand, I could just leave the code as-is, but I worry that this could create some infinite loops; if the error is ignored next() could be continuously called returning an Err each time. On the other, I could change the iterator so that on a subsequent call to next() after the error None is returned.
Are there guidelines/best practices for what to do as the implementer of the iterator this situation? I know some iterator adapters are aware of iterators that return Result<T, E>, but probably not all.
Solution 1:[1]
You could register a separate mapping for the base class. It is really strange that you have to do that, though. I use AutoMapper for mapping and haven't seen a quirk like this. This quirk kind of defeats the point of inheritance and seems like a bug in the utility.
BsonClassMap.RegisterClassMap<BaseEntity>(cm => {
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
cm.MapMember(x => x.Id).SetElementName("ProductID").SetSerializer(new StringSerializerFromInt());
//...
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | John Glenn |
