Fixing a bug here, where I wrongly relied on ExactSizeIterator::len
being correct, I noticed that ExactSizeIterator + TrustedLen
doesn't allow you to trust .len()
being correct.
Consider the following example:
#![feature(trusted_len)]
/// This module is sound.
pub mod a {
use std::iter::TrustedLen;
use std::marker::PhantomData;
pub struct EmptyIter<T> {
_phantom: PhantomData<T>,
}
impl<T> EmptyIter<T> {
pub const fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<T> Iterator for EmptyIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
}
unsafe impl<T> TrustedLen for EmptyIter<T> {}
}
/// This module is safe.
pub mod b {
use super::a::*;
pub struct Unit;
impl ExactSizeIterator for EmptyIter<Unit> {
fn len(&self) -> usize {
1
}
}
}
use std::iter::TrustedLen;
fn test<I>(iter: I)
where
I: Iterator<Item = b::Unit> + TrustedLen + ExactSizeIterator,
{
let (a, b) = iter.size_hint(); // we can trust `iter.size_hint()`
assert_eq!(a, 0);
assert_eq!(b, Some(0));
assert_eq!(iter.len(), 0, "but we cannot trust `iter.len()`");
}
fn main() {
test(a::EmptyIter::<b::Unit>::new());
}
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 8.64s
Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `0`: but we cannot trust `iter.len()`', src/main.rs:49:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Module a
is sound, module b
is safe, yet iter.len()
will return a wrong length.
I think the documentation is correct, but I still find this a bit surprising. Am I right that when I'm using TrustedLen
, I cannot trust .len()
? I guess I always have to use .size_hint()
?
Maybe TrustedLen
should be renamed to TrustedSizeHint
or something like that? Or a more explicit warning in the documentation could be added. Currently it reads:
Trait std::iter::ExactSizeIterator
[…]
Note that this trait is a safe trait and as such does not and cannot guarantee that the returned length is correct. This means that
unsafe
code must not rely on the correctness ofIterator::size_hint
. The unstable and unsafeTrustedLen
trait gives this additional guarantee.
This might be technically correct, but I still find it a bit confusing.