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...
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
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)
}
}