-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core::iter::Fuse
Does Not Fuse Iterators
#100518
Comments
This is a massive overstatement. Agreed, you can't depend on it for the soundness of your But that's normal. You can't depend on And, practically, I don't think copy-pasting a big "you can't rely on this for soundness" section onto every single non- |
This issue is specifically about the type
It would be like if The only reason the |
That simply means
I mean if you have a Vec<T, A> with a magic allocator that returns FUSE-backed memory-mapped memory that occasionally makes your values vanish (i.e. breaks the Alloc API contract), then yeah Vec will misbehave.
Fuse is useful because it gives generic code that needs a fused iterator to take an iterator that already is self-fusing or fuse it through that API. It glues |
This is not true, the API doesn't actually do anything for generic code that needs a fused iterator, despite The fact that not implementing the marker trait can be more useful than implementing it badly is a bug, is extremely unintuitive behavior, and because generic code needs to be able to handle the worst case always, makes this type useless. I don't see why The main problem is that |
The generic code takes any safe trait: someone implements this incorrectly -> it'll OOM, make your rockets explode mid-flight, irradiate people, drain your bank account or make planes fall out of the sky API contract violations are forbidden. The only difference is which sets of consequences a violation may have if someone does the forbidden thing. |
The return type is a
which is strictly not true. No caveats for bad impls are talked about, no exceptions are made, it is not a generic API taking |
Additionally this example does not apply, because there is a contract on the allocator for |
Also even if |
APIs are not required to state "does misbehave if traits aren't upheld". Adding those notes is a courtesy, usually added when multiple people tripped over a requirement. The absence of such statements is not a guarantee in itself. Otherwise we would be forced to document implementation details (this calls X, Y and Z and relies on their behavior) on even the most trivial things. E.g. iterator adapters don't tell you either which methods they forward. The default behavior is phrased in terms of If something is buggy somewhere then bugs will cascade until it hits something that is more defensive than whatever they cascaded through. GIGO, garbage in - garbage out.
Why are you assuming malice? This is a performance optimization that eliminates a branch inside loops which in turn can be vital for auto-vectorization.
They offer the same guarantees, so one can be substituted for the other. |
If they offer the same guarantees, at least one has incorrect docs, because a safe marker trait offers no guarantees, but |
Additionally, after some (admittedly limited) testing, I have not found a case where the specialization on |
They all promise the same thing, phrased slightly differently. |
Ah, I realize I was arguing from the perspective of safe code. For unsafe code the situation changes somewhat. At least the standard library knows its own implementation and so knows which safe parts unsafe code can trust. The situation is less clear for 3rd party unsafe code because it's not allowed to rely on implementation details. |
(And by extension, Fuse acts like that too, sine Which means if you actually need a fused If we did want To be honest, it is somewhat surprising to me that you can't trust Do we have a good way to measure how much this specialisation is saving us? Is it helpful only in the case of We have perf runs, but that won't help measure the cost of a change on The Ecosystem. |
|
Note that if you need an iterator to be forced fused for certain, you can always use struct DefinitelyNotFused<I>(I);
impl<I: Iterator> Iterator for DefinitelyNotFused<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
} And do That's really short because it doesn't forward anything but Because of that, this isn't really a problem specific to |
Hello libs-api folks! I had #100517 on me about this, which was closed, and now there's #102006 open for something different. But I think it needs an API decision for what, if anything, should be done, before I can do a review. A couple of possibilities:
|
Sorry about that previous PR, that was not a productive solution. What motivated me to make a PR actually making the behavior consistent was yet another complaint from someone that they found a case where they wanted a fused iterator from a generic iterator that may or may not actually fuse, but couldn’t use The fact that you must use a workaround like the above to get the desired behavior to me seems like it is a sign of a poorly designed API, and this is one of the rare cases where an std API can be fixed. |
In relation to the possible changes listed, #102006 currently uses a new |
Were they writing unsafe code? |
We discussed this in the libs-api meeting just now. Those of us present think we shouldn't change anything about this method and trait right now. The behaviour as expected (and documented), similar to how an invalid Hash implementation would break a HashMap. However, we'd be open to seeing concrete examples that show the value of an unsafe 'TrustedFusedIterator' trait. |
an unsafe |
This is acknowledged in
Iterator::fuse
withBut I believe this should not be the case.
Fuse
still incurs a memory overhead for all iterators, whether or not it specializes, and it's useless because it currently doesn't actually do anything in the general case.Example code:
It fails with
Since rustc 1.26.0, when this type was added, through current nightly (2022-08-13).
The text was updated successfully, but these errors were encountered: