Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to write a function that returns a RangeInclusive or its reverse iterator in stable Rust?

I have this code

use std::iter::Step;

fn main() {
    for i in mkiter(25, 20) {
        println!("{}", i);
    }
    for i in mkiter(10, 23) {
        println!("{}", i);
    }
}

fn mkiter<T>(start: T, end: T) -> Box<dyn Iterator<Item = T>>
where
    T: Step,
{
    if start > end {
        Box::new((end..=start).rev())
    } else {
        Box::new(start..=end)
    }
}

However, the compiler tells me that I am using an unstable feature:

error[E0658]: use of unstable library feature 'step_trait': recently redesigned
 --> main.rs:1:5
  |
1 | use std::iter::Step;
  |     ^^^^^^^^^^^^^^^
  |
  = note: see issue #42168 <https://github.com/rust-lang/rust/issues/42168> for more information

error[E0658]: use of unstable library feature 'step_trait': recently redesigned
  --> main.rs:13:10
   |
13 | where T: Step
   |          ^^^^
   |
   = note: see issue #42168 <https://github.com/rust-lang/rust/issues/42168> for more information

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.

But when I remove the trait bound…

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

fn main() {
    for i in mkiter(25, 20) {
        println!("{}", i);
    }
    for i in mkiter(10, 23) {
        println!("{}", i);
    }
}

fn mkiter<T>(start: T, end: T) -> Box<dyn Iterator<Item = T>> {
    if start > end {
        Box::new((end..=start).rev())
    } else {
        Box::new(start..=end)
    }
}

…it wants me to add it, which confuses me:

error[E0369]: binary operation `>` cannot be applied to type `T`
  --> main.rs:11:14
   |
11 |     if start > end {
   |        ----- ^ --- T
   |        T
   |
help: consider restricting type parameter `T`
   |
10 | fn mkiter<T: std::cmp::PartialOrd>(start: T, end: T) -> Box<dyn Iterator<Item = T>> {
   |            ++++++++++++++++++++++

error[E0599]: the method `rev` exists for struct `RangeInclusive<T>`, but its trait bounds were not satisfied
   --> main.rs:12:32
    |
12  |         Box::new((end..=start).rev())
    |                                ^^^ method cannot be called on `RangeInclusive<T>` due to unsatisfied trait bounds
    |
   ::: /home/rne/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/range.rs:345:1
    |
345 | pub struct RangeInclusive<Idx> {
    | ------------------------------ doesn't satisfy `RangeInclusive<T>: Iterator`
    |
    = note: the following trait bounds were not satisfied:
            `T: Step`
            which is required by `RangeInclusive<T>: Iterator`
            `RangeInclusive<T>: Iterator`
            which is required by `&mut RangeInclusive<T>: Iterator`
help: consider restricting the type parameter to satisfy the trait bound
    |
10  | fn mkiter<T>(start: T, end: T) -> Box<dyn Iterator<Item = T>> where T: Step {
    |                                                               +++++++++++++

error[E0277]: the trait bound `T: Step` is not satisfied
  --> main.rs:14:9
   |
14 |         Box::new(start..=end)
   |         ^^^^^^^^^^^^^^^^^^^^^ the trait `Step` is not implemented for `T`
   |
   = note: required for `RangeInclusive<T>` to implement `Iterator`
   = note: required for the cast from `RangeInclusive<T>` to the object type `dyn Iterator<Item = T>`
help: consider restricting type parameter `T`
   |
10 | fn mkiter<T: std::iter::Step>(start: T, end: T) -> Box<dyn Iterator<Item = T>> {
   |            +++++++++++++++++

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0369, E0599.
For more information about an error, try `rustc --explain E0277`.

How do I correctly implement a function that returns a RangeInclusive or its Rev iterator respectively for generic (integer) types in stable Rust?

# rustc --version                                                                                                                                                 2023-05-24 03:34:14
rustc 1.69.0 (84c898d65 2023-04-16)
# rustup update stable                                                                                                                                            2023-05-24 03:34:04
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'

  stable-x86_64-unknown-linux-gnu unchanged - rustc 1.69.0 (84c898d65 2023-04-16)

info: self-update is disabled for this build of rustup
info: any updates to rustup will need to be fetched with your system package manager

>Solution :

Requiring that T: Step is just a means to an end, you don’t actually care if T implements Step, you only care that RangeInclusive<T> is an iterator that yields T. And you can express that exactly:

fn mkiter<T>(start: T, end: T) -> Box<dyn Iterator<Item = T>>
where
    RangeInclusive<T>: Iterator<Item = T>,
...

The compiler still complains about T and Step, but that’s only because you need to follow the same principle to make .rev() work by constraining on DoubleEndedIterator as well:

fn mkiter<T>(start: T, end: T) -> Box<dyn Iterator<Item = T>>
where
    RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
...

Then there’s just the loose ends like adding T: PartialOrd so start > end works, and adding a lifetime to bypass the 'static-by-default of returning Box<dyn ...>. Here’s the final code:

fn mkiter<'a, T: 'a>(start: T, end: T) -> Box<dyn Iterator<Item = T> + 'a>
where
    T: PartialOrd,
    RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
{
    if start > end {
        Box::new((end..=start).rev())
    } else {
        Box::new(start..=end)
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading