-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
impl<A, B> IntoIterator for (A, B) as Zip #78204
Conversation
This makes it a little easier to `zip` iterators: ```rust for (x, y) in (xs, ys) {} // vs. for (x, y) in xs.into_iter().zip(ys) {} ``` You can iterate `(&mut xs, &ys)` for the conventional `iter_mut()` and `iter()`, respectively. This can also support arbitrary nesting, where it's easier to see the item layout than with arbitrary `zip` chains: ```rust for ((x, y), z) in ((xs, ys), zs) {} for (x, (y, z)) in (xs, (ys, zs)) {} // vs. for ((x, y), z) in xs.into_iter().zip(ys).zip(xz) {} for (x, (y, z)) in xs.into_iter().zip((ys.into_iter().zip(xz)) {} ``` Only tuple pairs are implemented for now. It's possible to implement longer tuples, but that would require a new iterator type to produce those tuple items. See itertools' [`Zip`] and rayon's [`MultiZip`] for prior art there. [`Zip`]: https://docs.rs/itertools/0.9.0/itertools/structs/struct.Zip.html [`MultiZip`]: https://docs.rs/rayon/1.4.1/rayon/iter/struct.MultiZip.html See also rust-lang/rfcs#870 and rust-lang/rfcs#930.
r? @estebank (rust_highfive has picked a reviewer for you, use r? to override) |
I may have gone overboard with example conversions throughout the compiler, but I can back any of that out if so. cc @yaahc @scottmcm @nikomatsakis from various conversations. |
One other note: in rayon, we also did the parallel equivalent of
That Lines 260 to 270 in 1d27267
I hope that won't ever be an issue for consuming tuples by value, per this PR, but I don't know... |
Do you see any that are particularly compelling in your opinion?
It's actually no difference there because |
Maybe I should back it up to just the |
As I expressed before, I'm a fan of this change. I really like being able to write |
I really like this change, but am slightly concerned about the insta-stabilization. r=me on the code, but defer to @rust-lang/libs @rfcbot fcp merge |
While I'm a fan of this change, I'm worried that there's another interpretation to Further consideration on programmer's part may reveal that the interpretation is not actually valid in general, because Rust iterators are not polymorphic like they are in e.g. Python. Nevertheless I think the overlap is big between the people who may initially assume the Python-like behaviour and people who are unlikely to come up to a similar conclusion. |
That would only be possible if
Dynamic typing + iteration means that Python tuples are pretty much interchangeable with lists (except for being shallow-immutable). I feel that’s not particularly a good thing we should try to emulate, especially with static typing. |
One one hand I have found lot of places in my code where this idea improves my code, shortening it and making it less noisy. On the other hand I agree with the Python Zen point that explicit is better than implicit. This idea introduces implicit parallel iteration, while a "zip" makes it explicit that we're having a parallel iteration. Implicit operations are handy but make the code less readable, so better to keep implicitness to only the most common operations and only where it doesn't cost too much (see Scala Implicits. Very powerful but they also cause troubles). So in a cost/advantages table I don't know if it's a good idea to introduce this idea. |
Another potential interpretation is the Cartesian product of the two iterators. Something like So while my initial reaction was ":tada: that's a lot nicer than |
Maybe there could be a method |
So if the consensus is for something more explicit, we could instead add An inherent tuple method |
☔ The latest upstream changes (presumably #78334) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
I wasn't aware of Python's interpretation. That seems like a negative. The cartesian product interpretation doesn't worry me as much because (ime) it is used far less frequently than zip. That said I agree that a free function or inherent tuple method might make for an interesting choice, although the need to import it is certainly a bit of a drag. But writing |
A free function named |
Just to add a little more context, I am also interested in adding the reverse, an I thought |
Yeah, I just wanted to point out a potential difference with how this is approached by other languages, but I'm otherwise hoping to see this change happen regardless. |
@cuviper We already have this? https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.unzip |
@SimonSapin Yes we have |
RE: #78204 (comment)
See #82917 -- just zipping a simple pair, no fancy variadics... |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to close, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. |
This makes it a little easier to `zip` iterators: ```rust for (x, y) in zip(xs, ys) {} // vs. for (x, y) in xs.into_iter().zip(ys) {} ``` You can `zip(&mut xs, &ys)` for the conventional `iter_mut()` and `iter()`, respectively. This can also support arbitrary nesting, where it's easier to see the item layout than with arbitrary `zip` chains: ```rust for ((x, y), z) in zip(zip(xs, ys), zs) {} for (x, (y, z)) in zip(xs, zip(ys, zs)) {} // vs. for ((x, y), z) in xs.into_iter().zip(ys).zip(xz) {} for (x, (y, z)) in xs.into_iter().zip((ys.into_iter().zip(xz)) {} ``` It may also format more nicely, especially when the first iterator is a longer chain of methods -- for example: ```rust iter::zip( trait_ref.substs.types().skip(1), impl_trait_ref.substs.types().skip(1), ) // vs. trait_ref .substs .types() .skip(1) .zip(impl_trait_ref.substs.types().skip(1)) ``` This replaces the tuple-pair `IntoIterator` in rust-lang#78204. There is prior art for the utility of this in [`itertools::zip`]. [`itertools::zip`]: https://docs.rs/itertools/0.10.0/itertools/fn.zip.html
Add function core::iter::zip This makes it a little easier to `zip` iterators: ```rust for (x, y) in zip(xs, ys) {} // vs. for (x, y) in xs.into_iter().zip(ys) {} ``` You can `zip(&mut xs, &ys)` for the conventional `iter_mut()` and `iter()`, respectively. This can also support arbitrary nesting, where it's easier to see the item layout than with arbitrary `zip` chains: ```rust for ((x, y), z) in zip(zip(xs, ys), zs) {} for (x, (y, z)) in zip(xs, zip(ys, zs)) {} // vs. for ((x, y), z) in xs.into_iter().zip(ys).zip(xz) {} for (x, (y, z)) in xs.into_iter().zip((ys.into_iter().zip(xz)) {} ``` It may also format more nicely, especially when the first iterator is a longer chain of methods -- for example: ```rust iter::zip( trait_ref.substs.types().skip(1), impl_trait_ref.substs.types().skip(1), ) // vs. trait_ref .substs .types() .skip(1) .zip(impl_trait_ref.substs.types().skip(1)) ``` This replaces the tuple-pair `IntoIterator` in rust-lang#78204. There is prior art for the utility of this in [`itertools::zip`]. [`itertools::zip`]: https://docs.rs/itertools/0.10.0/itertools/fn.zip.html
Add function core::iter::zip This makes it a little easier to `zip` iterators: ```rust for (x, y) in zip(xs, ys) {} // vs. for (x, y) in xs.into_iter().zip(ys) {} ``` You can `zip(&mut xs, &ys)` for the conventional `iter_mut()` and `iter()`, respectively. This can also support arbitrary nesting, where it's easier to see the item layout than with arbitrary `zip` chains: ```rust for ((x, y), z) in zip(zip(xs, ys), zs) {} for (x, (y, z)) in zip(xs, zip(ys, zs)) {} // vs. for ((x, y), z) in xs.into_iter().zip(ys).zip(xz) {} for (x, (y, z)) in xs.into_iter().zip((ys.into_iter().zip(xz)) {} ``` It may also format more nicely, especially when the first iterator is a longer chain of methods -- for example: ```rust iter::zip( trait_ref.substs.types().skip(1), impl_trait_ref.substs.types().skip(1), ) // vs. trait_ref .substs .types() .skip(1) .zip(impl_trait_ref.substs.types().skip(1)) ``` This replaces the tuple-pair `IntoIterator` in rust-lang#78204. There is prior art for the utility of this in [`itertools::zip`]. [`itertools::zip`]: https://docs.rs/itertools/0.10.0/itertools/fn.zip.html
This makes it a little easier to
zip
iterators:You can iterate
(&mut xs, &ys)
for the conventionaliter_mut()
anditer()
, respectively. This can also support arbitrary nesting, whereit's easier to see the item layout than with arbitrary
zip
chains:Only tuple pairs are implemented for now. It's possible to implement
longer tuples, but that would require a new iterator type to produce
those tuple items. See itertools'
Zip
and rayon'sMultiZip
forprior art there.
See also rust-lang/rfcs#870 and rust-lang/rfcs#930.