-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Make iter::Fuse
truly zero cost
#70374
Conversation
FWIW, a similar optimization to |
Sorry, I don’t have much bandwidth available to review Rust PRs. Please consider letting the highfive bot pick a reviewer, or picking one based on https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust.json Per |
Btw, I think this PR conflicts with #70366 which also rewrites |
r? @matthewjasper (due the new specialization moratorium) and cc @cuviper |
I think the two approaches could be combined, by having |
I don't think that it is desirable, AFAIK iterator adapters don't drop internal iterators. Restructuring |
I've noted that #31844 still has unresolved questions about associated types, including whether they should allow specialization at all.
I like this idea, if we do allow associated type specialization, and assuming we also decide early drops are ok.
In general they don't, because it would require more code and extra wrappers to drop early, adding unnecessary cost. Some do drop necessarily, like
It is an observable change, but that doesn't necessarily mean it's part of the API contract. I think someone would be on shaky ground to rely on exactly when |
FYI, the other PR has been r+'d. I do like this PR, though, assuming that T-compiler is ok with using specialization to pick a type like this. I think having only one implementation of Iterator for Fuse sounds great, and a nice demonstration of type-based design to have the You might even be able to do this by just changing |
☔ The latest upstream changes (presumably #70404) made this pull request unmergeable. Please resolve the merge conflicts. |
Okay, I tried to implement this approach, but it turns that it is impossible (at least without negative trait bounds). In order to utilize layout optimizations, we need to keep internal iterator in #[inline]
default fn next(&mut self) -> Option<<I as Iterator>::Item> {
let next = self.iter.as_mut()?.next();
if next.is_none() {
self.iter = None;
}
next
} Remaking this method in desired requires us to write in the following way: default fn next(&mut self) -> Option<<I as Iterator>::Item> {
let next = self.iter.as_mut()?.next(); // assuming we already rewrote `as_mut`
if next.is_none() {
self.iter = Err(???);
}
next
} And here comes the question: what should we put in place of |
Right now you have |
It still doesn't solve the problem since unspecialized version still has to work with all iterators and thus make a value of uninhabitated type. |
I meant something like: if next.is_none() {
Flag::set(&mut self.iter);
} This can be a no-op for |
Since libs team seems to agree with change in dropping semantics, this PR is unlikely to be merged, so I close it. |
As documentation for
std::iter::FusedIterator
says:However, the "no performance penalty" part seems a bit questionable:
Fuse
always keepsdone
flag no matter if wrapped iterator is fused or not. For a fused iterator it makes no sense to actually keep this flag, yetFuse
blindly attaches it to everything.This PR resolves the issue: the new implementation guarantess that
Fuse
on fused iterator just forwards methods to wrapped iterator and carries no additional data. This may bring benefit for code that operates on generic iterator and immediately calls.fuse()
on it.Possible reasons not to merge this PR (in no particular order):
size_hint
for exhausted iterator even if internal one has impropersize_hint
implementation. However, now this opportunity is not used, andFuse::<I: FusedIterator>::size_hint
just forwards method to wrapped iterator.Debug
output ofFuse
. However, it can be easily mitigated with customDebug
impl.Iterator
.r? @SimonSapin