迭代器是否定义错误后 `next()` 应该 return 的语义?

Do iterators define semantics for what `next()` should return after an error?

基本问题是:如果 Iterator 特征的实现 return 是一个 Result,迭代器应该在 returned 错误后做什么next() 这使得无法继续迭代?

一些上下文:

作为一个学习项目,我正在尝试实现一个解析库来解析 STUN 消息,如 RFC5389 在 Rust 中定义的那样。 STUN 是一种二进制网络协议,因此我将解析字节片。具体来说,该协议规定可以将零个或多个动态大小的属性编码到消息中。因此,我正在尝试构建一个迭代器,它可用于迭代字节,为每个属性生成子切片。

因此,我有这样的事情...

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 }));
    }
}

这里有很多地方可能出错,我在 if 语句中包含了一个示例。如果出现问题,则表明正在解析的字节流格式错误,因此没有理由继续尝试解析。

一方面,我可以让代码保持原样,但我担心这会产生一些无限循环;如果错误被忽略,next() 可以连续调用 return 每次 Err。另一方面,我可以更改迭代器,以便在错误 None 被 return 编辑后对 next() 的后续调用。

在这种情况下,作为迭代器的实现者,是否有 guidelines/best 实践?我知道一些迭代器适配器知道 return Result<T, E> 的迭代器,但可能不是全部。

让我们看看the docs怎么说:

Returns None when iteration is finished. Individual iterator implementations may choose to resume iteration, and so calling next() again may or may not eventually start returning Some(Item) again at some point.

Iterator 合同而言,您可以随时 return 来自 next() 的任何东西——甚至 returning Some在之前 returning None.

之后

(FusedIterator 标记表示迭代器的 next() 承诺仅 return None 在它 returned None 之前.)

综上所述,没有必要的行为。你在这里甚至不是在谈论 None,你在谈论 Some(Err(_)),所以即使 FusedIterator 的合同不是一个单独的东西,而是由 [=13] 强制执行=],无论您选择做什么,从技术上讲,您在这里仍然没问题。

有几个 Iterator 实用程序可以专门与 Results 序列交互。例如,您可以将 Result<T, E> 的迭代器 .collect() 转换为 Result<Vec<T>, E>,但这是假设您只想在没有发生任何错误的情况下查看生成的序列。

我认为有一些 明智的 事情你可以在产生错误后做:

  1. 如果失败是永久性的,则之后放弃 None。如果您愿意,以下 next() 调用可以重新启动操作。 (或者,return None 永远,然后实现 FusedIterator 来传达这一点是个好主意。)
  2. 如果失败是暂时的(可以重试操作),那么您可以再试一次,让调用者自己决定何时停止。
  3. 取决于配置或方法调用的先前选项之一。例如,您可以为迭代器的 constructor/factory 设置一个 retries 参数,指示在放弃和 returning None 之前有多少次连续失败 return,或者您可以在您的类型中添加 fn retry()。如果 next() return 出现错误,它将在接下来的调用中 return None 除非立即调用 retry()之前,在这种情况下它会重试该操作。

无论您决定什么,清楚地记录下来。 第二种情况有可能导致 never-ending 一系列错误,这使得不重试操作成为可能最安全的做法。

话虽如此,我已经编写了一个类似类型的迭代器,并且在出现错误的情况下重试。我的一些调用者在每个元素上使用 ? 来避免错误,其他调用者实现重试逻辑,如果发生太多错误,最终会放弃。