Closed
Description
struct S(bool);
impl Iterator for S {
type Item = ();
fn next(&mut self) -> Option<()> {
self.0 = !self.0;
if self.0 {
Some(())
} else {
None
}
}
}
impl std::iter::FusedIterator for S {}
fn main() {
let mut x = S(false).fuse();
for _i in &mut x {}
// x is fused, so it must be empty.
assert_eq!(x.next(), None);
}
This means unsafe code may not rely on iter.fuse()
to actually fuse the iterator if iter
is generic, which is unexpected to me.
I personally would like us to either add a note to Iterator::fuse
mentioning this or to remove the specialization for std::iter::Fused
.
Activity
[-]`Iterator::fuse` is not guaranteed to actually fuse the given iterator.[/-][+]`Iterator::fuse` is not guaranteed to fuse a generic iterator.[/+]amorison commentedon Apr 7, 2021
Well, by implementing
std::iter::FusedIterator
, you declare thatS
is already fused even though it isn't. You should remove the lineimpl std::iter::FusedIterator for S {}
to get the expected behaviour. The only thing implementingFusedIterator
onT
does is tellIterator::fuse
that there is nothing to do because the typeT
is already fused. It doesn't fuse the iterator.(As a side note, unsafe code should not blindly rely on safe code, especially traits implementation, precisely because it is impossible to automatically check that implementations are logically correct.)
EDIT : maybe I misunderstood your request, are you suggesting
FusedIterator
be deprecated and ignored byfuse
because it can lead tofuse
not working properly when wrongly implemented? That doesn't seem like a good idea to me, correct implementation of any trait is necessarily the burden of the user of that trait. Maybe the documentation could be more clear but it seems good to me as is to be honest.ExpHP commentedon Apr 9, 2021
Of course, the option of making the trait unsafe was discussed as part of the RFC, but it seems that the use case for the guarantees of
Fuse
inunsafe
code were unclear at the time.That said, I do kind of see how somebody might look of the signature of
fuse
:and think, "ah, this returns a standard library type, so I should be able to trust this on an arbitrary iterator," without considering that
Fuse<I>
uses specialization on a safe trait. Perhaps this merits a warning in the documentation. (Edit: Ah, this is precisely as you suggested!)workingjubilee commentedon Apr 22, 2021
I honestly imagine there is also a lot of unsafe code that relies on
Eq
implementations being correct for generic types, no? Is this different?ExpHP commentedon Apr 22, 2021
I feel that there is a difference, due to way that specialization impacts this. The danger of reliance on safe traits in unsafe code is so omnipresent that I would hope that most people who audit unsafe code already look for things like bad
Eq
impls. I know that when I seeT: Eq
in an unsafe function, things that immediately come to mind are:On the other hand, if I were auditing unsafe code and saw a function take
T: Iterator
and callIterator::fuse
, I might think:fuse
method and use a different/modified instance ofSelf
that returns different items.fuse
method and make it panic.std::iter::Fuse
, I can see myself easily mistakenly thinking that, at the very least, the result is properly fused. I might not think of falseFusedIterator
implementations since that trait is not mentioned anywhere in the code or even in the docs ofIterator::fuse
amorison commentedon Apr 23, 2021
I guess adding something along the line of
in the doc of
Iterator::fuse
would be enough of a warning? Maybe a similar sentence in the doc ofFusedIterator
itself, but I feel like the current documentation is clear on the expected contract. If this sounds like a good idea, I could make a PR.workingjubilee commentedon Apr 23, 2021
Sounds good to me!
Rollup merge of rust-lang#84489 - amorison:issue-83969-fix, r=yaahc