Skip to content
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

Replace IteratorExt::zip with tuple iteration #870

Closed
wants to merge 6 commits into from

Conversation

oli-obk
Copy link
Contributor

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

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
Copy link
Contributor

Gankra commented Feb 16, 2015

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
Copy link
Contributor Author

oli-obk commented Feb 16, 2015

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
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
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
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
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
Copy link

pczarn commented Feb 16, 2015

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
Copy link
Contributor

Gankra commented Feb 16, 2015

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

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 17, 2015

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
Copy link
Contributor

llogiq commented Feb 17, 2015

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
Copy link
Contributor Author

oli-obk commented Feb 17, 2015

@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
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
Contributor

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

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 17, 2015

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

@Ericson2314
Copy link
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
Copy link
Contributor Author

oli-obk commented Feb 17, 2015

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
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
Copy link
Contributor

Gankra commented Feb 18, 2015

I've come around to this.

Let's lean into the magic together, everyone

@Gankra
Copy link
Contributor

Gankra commented Feb 18, 2015

Cool consequence when extend is upgraded to use IntoIterator:

some_map.extend((keys, vals))

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 23, 2015

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
Copy link
Contributor

tikue commented Feb 28, 2015

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

@eddyb
Copy link
Member

eddyb commented Feb 28, 2015

👍 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
Member

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

@vwim
Copy link

vwim commented Feb 28, 2015

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

@petrochenkov
Copy link
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
Copy link
Contributor

brson commented Mar 3, 2015

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.

None yet