-
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
Override Cycle::try_fold #62737
Override Cycle::try_fold #62737
Conversation
Ping from triage, any updates? @scottmcm |
I'm staring at the The Could it make sense to just loop over the version that checks for empty, to avoid spinning on the clone if the iterator does end up empty somehow? Or maybe I'm worried for nothing... |
@scottmcm Even if The |
I was thinking of something like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=71cd4c47937fc4f290ec9b1c65867435 But even that doesn't have two-or-more non-empty cycles followed by an empty one. So I have to go all the way to this: use std::cell::Cell;
fn main() {
let n = &Cell::new(5);
let c = &Cell::new(0);
let it = std::iter::from_fn(|| {
let x = c.get();
if x == 0 {
let x = n.get();
n.set(x - 1);
c.set(x);
None
} else {
c.set(x - 1);
Some(x)
}
});
let it = it.cycle();
it.for_each(|x| {
dbg!(x);
});
} https://play.rust-lang.org/?edition=2018&gist=cd0b0f3e7187e8fa5aecb042f547483a That's certainly a bit of a stretch, but it's easy to write and kindof an interesting way to do a triangular iterator. So maybe it'd be safer to just put the loop around the empty-checked version? And it feels like that outer loop isn't the place where this is improving perf, so it shouldn't cause any of the benchmarks to get materially worse. (I normally am fine with people getting slightly-odd behaviour from mutable closures, but introducing non-termination is scary to me.) I guess I should also ping @rust-lang/libs to see if they have conventions for how to handle people doing unusual things in FnMut closures (like |
Ah, so intentional misuse of |
Based only on the Going out of one’s way to share mutable state between clones of the iterator (through |
Ok, I'll consider that libs approval for the somewhat-contrived behaviour change here. We have a good 10 weeks to revert if someone else has a strong opinion the other way. @bors r+ I wonder if Iterator should document this expectation on Clone, like how Hash does? |
📌 Commit 688c112 has been approved by |
…tmcm Override Cycle::try_fold It's not very pretty, but I believe this is the simplest way to correctly implement `Cycle::try_fold`. The following may seem correct: ```rust loop { acc = self.iter.try_fold(acc, &mut f)?; self.iter = self.orig.clone(); } ``` ...but this loops infinitely in case `self.orig` is empty, as opposed to returning `acc`. So we first have to fully iterate `self.orig` to check whether it is empty or not, and before _that_, we have to iterate the remaining elements of `self.iter`. This should always call `self.orig.clone()` the same amount of times as repeated `next()` calls would. r? @scottmcm
…tmcm Override Cycle::try_fold It's not very pretty, but I believe this is the simplest way to correctly implement `Cycle::try_fold`. The following may seem correct: ```rust loop { acc = self.iter.try_fold(acc, &mut f)?; self.iter = self.orig.clone(); } ``` ...but this loops infinitely in case `self.orig` is empty, as opposed to returning `acc`. So we first have to fully iterate `self.orig` to check whether it is empty or not, and before _that_, we have to iterate the remaining elements of `self.iter`. This should always call `self.orig.clone()` the same amount of times as repeated `next()` calls would. r? @scottmcm
Rollup of 4 pull requests Successful merges: - #62737 (Override Cycle::try_fold) - #63505 (Hash the remapped sysroot instead of the original.) - #63559 (rustc_codegen_utils: account for 1-indexed anonymous lifetimes in v0 mangling.) - #63621 (Modify librustc_llvm to pass -DNDEBUG while compiling.) Failed merges: r? @ghost
It's not very pretty, but I believe this is the simplest way to correctly implement
Cycle::try_fold
. The following may seem correct:...but this loops infinitely in case
self.orig
is empty, as opposed to returningacc
. So we first have to fully iterateself.orig
to check whether it is empty or not, and before that, we have to iterate the remaining elements ofself.iter
.This should always call
self.orig.clone()
the same amount of times as repeatednext()
calls would.r? @scottmcm