Don't delegate to default implementations of ExactSizeIterator by yotamofek · Pull Request #149384 · rust-lang/rust (original) (raw)

FWIW, if you try

#![feature(binary_heap_into_iter_sorted)] pub fn demo(it: &std::collections::binary_heap::IntoIterSorted) -> usize { let (low, high) = it.size_hint(); assert_eq!(Some(low), high); low }

in playground with "Show MIR" https://play.rust-lang.org/?version=nightly&mode=release&edition=2024&gist=a074bb1d7524e51ab5cb16746fa66505

Then you'll see that the assert actually optimizes out before we even get to LLVM

bb0: {
    _0 = copy ((((*_1).0: std::collections::BinaryHeap<i32>).0: std::vec::Vec<i32>).1: usize);
    StorageLive(_6);
    _6 = Le(copy _0, const <i32 as std::mem::SizedTypeProperties>::MAX_SLICE_LEN);
    assume(move _6);
    StorageDead(_6);
    _5 = Option::<usize>::Some(copy _0);
    StorageLive(_2);
    _2 = copy _5;
    // DBG: _3 = &_5;
    // DBG: _4 = &_2;
    StorageDead(_2);
    return;
}

(It still has some silly leftovers -- probably due to phase ordering -- but there's no assert any more, just the comparison to tell LLVM it's UB to have a too-high length so it can optimize based on that.)

So overall I don't know how much it's worth bothering adding a bunch of extra code in the library.

That said, at the same time the overrides that just call an existing self.len() seem kinda reasonable anyway, so I dunno.


An alternative: this is just a sanity check, so I wonder if we might say "hey, that all-the-time assert is silly" instead, and maybe (in a different PR that would probably need a team nomination) replace the existing

#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn len(&self) -> usize {
    let (lower, upper) = self.size_hint();
    // Note: This assertion is overly defensive, but it checks the invariant
    // guaranteed by the trait. If this trait were rust-internal,
    // we could use debug_assert!; assert_eq! will check all Rust user
    // implementations too.
    assert_eq!(upper, Some(lower));
    lower
}

with

#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rust_inherit_overflow_checks]
fn len(&self) -> usize {
    let (lower, upper) = self.size_hint();
    // If overflow checks are on, check that the size hint isn't malformed.
    // An incorrect `size_hint` isn't UB, so we don't *have* to check,
    // and thus in default release mode we don't to save time.
    if let Some(upper) = upper {
        _ = upper - lower;
    }
    lower
}

on the theory that in release mode it's just not ever worth bothering with this check.


Let me know how you're feeling,
@rustbot author