Skip to content

Replace IteratorExt::zip with tuple iteration - #870

Closed
oli-obk wants to merge 6 commits into
rust-lang:masterfrom
oli-obk:master
Closed

Replace IteratorExt::zip with tuple iteration#870
oli-obk wants to merge 6 commits into
rust-lang:masterfrom
oli-obk:master

Conversation

@oli-obk

@oli-obk oli-obk commented Feb 16, 2015

Copy link
Copy Markdown
Contributor
for (x, y, z) in (a, b, c) {
   // do something smart
}

Should iterate over a, b and c (if all of them implement IntoIterator) and return the current element of each of them in x, y, and z until any of a, b or c return None.

internals.rust-lang.org discussion

Rendered

@Gankra

Gankra commented Feb 16, 2015

Copy link
Copy Markdown
Contributor

Is this not a bit of a regression since you can theoretically indefinitely Zip, whereas with this you'll run into Tuple impl size limits quickly?

Otherwise my only objection to this is the vague "Rust is becoming more magic" problem (that I am incredibly guilty of exacerbating). But maybe this is more intuitive that there being a method called Zip?

@oli-obk

oli-obk commented Feb 16, 2015

Copy link
Copy Markdown
Contributor Author

If that would help my case for this RFC, I could collect a list of cries for help on stackoverflow of people trying to zip in several languages without knowing the keyword zip.

@shepmaster

Copy link
Copy Markdown
Member

👎 I'll agree that I prefer to use a function named zip. Ruby and Clojure (two languages I am familiar with) have zip and I think it's reasonable to expect people to know the name.

Also your comment above:

I could collect a list of cries for help on stackoverflow of people trying to zip in several languages without knowing the keyword zip

Doesn't really jive with your statement in the forum:

true... although I'd assume stack-overflow solves [that it's hard to discover that tupling zips the iterators] after the question has been asked a few times

@nikomatsakis

Copy link
Copy Markdown
Contributor

@gankro regarding the regression, I'm not sure I follow. Isn't todays zip just equivalent to a 2-tuple here? In particular, can't you nest tuples to achieve arbitrary iteration?

for (x, (y, z)) in (xs, (ys, zs)) { ... }

Or am I missing something?

@nikomatsakis

Copy link
Copy Markdown
Contributor

(I do think that as far as "guessability" goes, zip is probably more guessable than tuples, since it is used in so many languages.)

@nikomatsakis

Copy link
Copy Markdown
Contributor

I have mixed feelings. I feel like this approach is beautifully concise, but it may be a learning hazard. On the other hand, that's true for a lot of iterator magic (e.g., collecting into a Result<Vec<_>, _>), and once you know it, it's just so perty. Guess I'd just want to get a sense for what people at large feel.

(That said, for some reason now that for item in collection works, I keep sort of expecting for (a, b) in (as, bs) to work. Probably because I wind up typing the parallel structures of for item in collection, for &item in &collection, etc so often.)

@pczarn

pczarn commented Feb 16, 2015

Copy link
Copy Markdown

Perhaps I would like to have for (x, y, z) in (a, b, c).transpose() {} which is not as magical as IntoIterator, as I said once in the forum.

@Gankra

Gankra commented Feb 16, 2015

Copy link
Copy Markdown
Contributor

@nikomatsakis. Ah yes, you're right. Didn't occur to me that that would "just work".

@oli-obk

oli-obk commented Feb 17, 2015

Copy link
Copy Markdown
Contributor Author

i could live with .zip() on tuples, but I prefer to implement IntoIterator
@shepmaster of course it fits together. Many languages don't support such a feature (example: search for c++, iterate, multiple), and require third party libraries + some uncomfortable syntax. The first thing I tried when i needed zipping was to use tuples, which didn't work, but for me seemed like the thing that should work. Not sure if the general intuition comes to the same result.

@llogiq

llogiq commented Feb 17, 2015

Copy link
Copy Markdown
Contributor

What if someone implements IntoIter for arbitrary same-size tuples? I think it would not necessariily be ambiguous (because of the match on (x, y, z)), but it could throw off fellow developers who may be unsure what the code is doing.

@oli-obk

oli-obk commented Feb 17, 2015

Copy link
Copy Markdown
Contributor Author

@llogiq: i guess that's similar to someone implementing IntoIter for [T] or something. The compiler would tell you this exists already.

@petrochenkov

Copy link
Copy Markdown
Contributor

I think, bad searchability is a relatively weak argument against this proposal.
It usually works like: google "rust zip iterator" -> the first result is a stackoverflow answer with thousands of upvotes, describing the tuple notation in details. As an example, try to search "perl break", "python ternary operator" or "c string split".
(At least that is what I do when I have to use languages that I'm unfamiliar with, and it always works for basic things like zip even if the syntax is completely different.)

@Ericson2314

Copy link
Copy Markdown
Contributor

Could this be done with polymorphic impls, or does it need to hefty syntactic support?

@oli-obk

oli-obk commented Feb 17, 2015

Copy link
Copy Markdown
Contributor Author

@Ericson2314 well we need some macro magic for tuples with 1 to n elements. n probably being something around 10

@Ericson2314

Copy link
Copy Markdown
Contributor

I would have assumed this would work:

impl<I: IntoIter, J: IntoIter> impl IntoIter for (I, J) {
  type IntoIter= (<I as IntoIter>::IntoIter, <J as IntoIter>::IntoIter);
  ...
}

impl<I: Iter, J: Iter> impl Iter for (I, J) {
  type Item = (<I as IntoIter>::Item, <J as IntoIter>::Item);
  ...
}

and so on for larger tuples.

@oli-obk

oli-obk commented Feb 17, 2015

Copy link
Copy Markdown
Contributor Author

yes, that will be generated by macros, as to not repeat ourselves. I have a sample implementation in the rfc. it's rather blunt, but if the rfc is accepted i will actually put in an effort to make it nice

@Ericson2314

Copy link
Copy Markdown
Contributor

Ok, just checking. I thought people were implying the feature itself would require compiler syntactic support, when really it's just that its implementation would benefit from it, but doesn't even need it.

I'm more for this then. Sure its a bit spooky but isn't proposing anything that couldn't be done already (baring coherence).

@Gankra

Gankra commented Feb 18, 2015

Copy link
Copy Markdown
Contributor

I've come around to this.

Let's lean into the magic together, everyone

@Gankra

Gankra commented Feb 18, 2015

Copy link
Copy Markdown
Contributor

Cool consequence when extend is upgraded to use IntoIterator:

some_map.extend((keys, vals))

@oli-obk

oli-obk commented Feb 23, 2015

Copy link
Copy Markdown
Contributor Author

I added another alternative: requiring .zip() which is discoverable and extendable to similar functions like .product() and .flatten(). Also, this rfc can be post-1.0 then.

@tikue

tikue commented Feb 28, 2015

Copy link
Copy Markdown
Contributor

Ah...this is gorgeous! It's so obvious that I'm surprised it hasn't been brought up sooner.

@eddyb

eddyb commented Feb 28, 2015

Copy link
Copy Markdown
Contributor

👍 This is amazing and I want it.
Those method chains can get rather tedious and I wish we'd use more operators for iterators, but without custom ones it would get confusing fast.

@emberian

Copy link
Copy Markdown
Contributor

This is cute. I agree fully with @nikomatsakis though. I feel pretty hesitant about this.

@vwim

vwim commented Feb 28, 2015

Copy link
Copy Markdown

Looks nice but I think this should be solved in a more structural way instead of applying magic.

@petrochenkov

Copy link
Copy Markdown
Contributor

I like how intuitive this notation is.

How do you iterate through a list of elements?

for x in xs {}

How do you iterate through two lists simultaneously?
Well,

for x, y in xs, ys {}

, but with some parentheses required.
Simultaneous iteration is a very simple concept, much simpler than the underlying iterator machinery and zip itself. Someone should try it on children and report, I think the results will be positive :D

@brson brson mentioned this pull request Mar 3, 2015
@brson

brson commented Mar 3, 2015

Copy link
Copy Markdown
Contributor

While this is a clever idea with some promise, it is also very much a nice-to-have, and I'm strongly inclined to exercise discretion in adding new features to Rust at this time. I've opened #930 to let this idea continue to bake, but am closing this PR. Thank you.

@brson brson closed this Mar 5, 2015
cuviper added a commit to cuviper/rust that referenced this pull request Oct 21, 2020
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.