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

step_by() API change #43063

Closed
leonardo-m opened this issue Jul 5, 2017 · 17 comments
Closed

step_by() API change #43063

leonardo-m opened this issue Jul 5, 2017 · 17 comments
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@leonardo-m
Copy link

leonardo-m commented Jul 5, 2017

In past step_by() needed an argument of the same type of the range:

#![feature(iterator_step_by)]
fn main() {
    for i in (1u32 .. 10u32).step_by(2u32) {
        println!("{}", i);
    }
}

But now it gives an error and asks for an usize:

error[E0308]: mismatched types
 --> ...\test.rs:3:38
  |
3 |     for i in (1u32 .. 10u32).step_by(2u32) {
  |                                      ^^^^ expected usize, found u32

Is this desired (and I have to change all my Nightly code)?

This is especially bad when I've used negative step values:

(a - 2 .. 1).step_by(-2)

Now you need to write something like:

(2 + a % 2 .. a - 1).rev().step_by(2)

See also #43012

@bstrie bstrie added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Jul 5, 2017
@bstrie
Copy link
Contributor

bstrie commented Jul 5, 2017

Is this a deliberate regression? Trying to follow along with the discussion for the new step_by, but it seems quite tangled and I don't see a definitive RFC.

cc @SimonSapin based on your comments in #43012

@SimonSapin
Copy link
Contributor

#27741 (comment) looks like a decision to move step_by to the Iterator trait, based on Iterator::nth. Since nth takes usize, it seems expected to me that the new step_by does too.

One way to think of it is as a number of iterations rather than a "distance" between points.

@leonardo-m
Copy link
Author

There are situations where I'd like to write:

for i in (20u32 .. 0u32).step_by(-2) { }

That is, negative stepping even when the range is on unsigned values :-) (This code was disallowed even by the old step_by, but it's handy).

@kennytm
Copy link
Member

kennytm commented Jul 5, 2017

@leonardo-m This has been discussed before and is quickly dismissed because it is confusing. Should (20 .. 0).step_by(-2) produce [20, 18, ..., 2] or [19, 17, ..., 1] or [18, 16, ..., 0]? (Similar confusion exists for (0 .. 4).step_by(-2).)

I guess you need to write your own iterator or function producing StepBy<Either<Range, Rev<Range>>> in those cases where the step can be positive or negative.

@leonardo-m
Copy link
Author

I think it should give [20, 18, 16, 14, 12, 10, 8, 6, 4, 2].

Do you suggest to close this issue down then?

@kennytm
Copy link
Member

kennytm commented Jul 5, 2017

@leonardo-m I do think Iterator::step_by taking usize is expected. There is no "same type of the range" if you want to also support ["x", "y", "z"].iter().step_by(2).

@leonardo-m
Copy link
Author

It's a quite limiting API. What about using isize?

@cramertj
Copy link
Member

cramertj commented Jul 5, 2017

@leonardo-m What would that do? Just Iterator::rev and then step_by?

@leonardo-m
Copy link
Author

It's a single operation. ['a', 'b', 'c', 'd', 'e'].iter().step_by(-2) could give something like ['e', 'c', 'a'].

@cramertj
Copy link
Member

cramertj commented Jul 5, 2017

Right-- I'm asking how that operation should be implemented generically for iterators. It seems like the most obvious way would be to call Iterator::rev and then step_by, which you could just do yourself: ['a', 'b', 'c', 'd', 'e'].iter().rev().step_by(2).

@leonardo-m
Copy link
Author

The "-2" isn't always hard-coded, in general it's a variable, and it can change at run-time. So you don't know if you should put a rev() there or not. If you look in other threads people are proposing solutions to this... a way to reverse the scanning conditionally...

@SimonSapin
Copy link
Contributor

As I’ve said elsewhere, I ended up writing this for Servo:

struct MaybeReverse<I> {
    iter: I,
    reverse: bool,
}

impl<I: DoubleEndedIterator> Iterator for MaybeReverse<I> {
    type Item = I::Item;

    fn next(&mut self) -> Option<I::Item> {
        if self.reverse {
            self.iter.next_back()
        } else {
            self.iter.next()
        }
    }
}

@leonardo-m
Copy link
Author

leonardo-m commented Jul 5, 2017

Yes, I saw that...

step_by() is going to be a very commonly used function, and I think most times it will be called on ranges of integral values. Currently you can't use it ( #43064 ). If you want Rust code to compile fast you need to keep extrinsic complexity low and abstraction as low as possible, especially for commonly used things.

@SimonSapin
Copy link
Contributor

@leonardo-m #43064 is not controversial. I’m working on a PR.

@SimonSapin
Copy link
Contributor

#43077 should fix the performance problem.

@leonardo-m
Copy link
Author

Thank you. In my last comment I was talking about general Rust code compilation speed (while Issue 43064 is about run-time performance).

Code where you don't know at compile-time if you need to step forwards or backwards exists, I think I have such code in some matrix scanning strategies, but it's uncommon.

@leonardo-m
Copy link
Author

I close this down because most API discussions already happened elsewhere, and they are settled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants