as I understand it, Calling the chain method on an iterator consumes it and returns a new type. This is a problem, because I need to chain multiple iterators together inside a for loop. My first attempt:
pub fn convert(s: String, num_rows: i32) -> String {
let mut iter = s.chars().enumerate().filter_map(|(i, c)| {
if i % ((num_rows as usize - 1)*2) == 0 {Some(c)} else {None}});
for n in 1..num_rows as usize {
iter = iter.chain(
s.chars().enumerate().filter_map(|(i,c)| {
let cycle = (num_rows as usize - 1)*2;
if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
})
);
}
iter.collect()
}
This gives me an error:
error[E0308]: mismatched types
--> src/lib.rs:5:16
|
2 | let mut iter = s.chars().enumerate().filter_map(|(i, c)| {
| _____________________________________________________-
3 | | if i % ((num_rows as usize - 1)*2) == 0 {Some(c)} else {None}});
| | -
| | |
| |______________________________________________________________________the expected closure
| the found closure
4 | for n in 0..num_rows as usize {
5 | iter = iter.chain(
| ________________^
6 | | s.chars().enumerate().filter_map(|(i,c)| {
7 | | let cycle = (num_rows as usize - 1)*2;
8 | | if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
9 | | })
10 | | );
| |_________^ expected struct `FilterMap`, found struct `std::iter::Chain`
|
= note: expected struct `FilterMap<Enumerate<Chars<'_>>, _>`
found struct `std::iter::Chain<FilterMap<Enumerate<Chars<'_>>, _>, FilterMap<Enumerate<Chars<'_>>, [closure@src/lib.rs:6:46: 9:14]>>`
no big deal, I thought. I'll just let it infer the type inside the loop, like this:
pub fn convert(s: String, num_rows: i32) -> String {
let mut iter;
for n in 0..num_rows as usize {
iter = iter.chain(
s.chars().enumerate().filter_map(|(i,c)| {
let cycle = (num_rows as usize - 1)*2;
if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
})
);
}
iter.collect()
}
This gives me a new error saying I need to declare a type:
error[E0282]: type annotations needed
--> src/lib.rs:4:16
|
2 | let mut iter;
| -------- consider giving `iter` a type
3 | for n in 0..num_rows as usize {
4 | iter = iter.chain(
| ^^^^ cannot infer type
|
= note: type must be known at this point
So, after that I tried adding in the type annotations from the earlier compiler error:
use core::str::Chars;
use core::iter::*;
let mut iter: Chain<FilterMap<Enumerate<Chars<'_>>, _>, FilterMap<Enumerate<Chars<'_>>, _>>;
This looks quite ugly, and it still didn't work:
error[E0308]: mismatched types
--> src/lib.rs:6:16
|
6 | iter = iter.chain(
| ________________^
7 | | s.chars().enumerate().filter_map(|(i,c)| {
8 | | let cycle = (num_rows as usize - 1)*2;
9 | | if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
10 | | })
11 | | );
| |_________^ expected struct `std::iter::FilterMap`, found struct `std::iter::Chain`
|
= note: expected struct `std::iter::Chain<std::iter::FilterMap<std::iter::Enumerate<Chars<'_>>, _>, std::iter::FilterMap<std::iter::Enumerate<Chars<'_>>, _>>`
found struct `std::iter::Chain<std::iter::Chain<std::iter::FilterMap<std::iter::Enumerate<Chars<'_>>, _>, std::iter::FilterMap<std::iter::Enumerate<Chars<'_>>, _>>, std::iter::FilterMap<std::iter::Enumerate<Chars<'_>>, [closure@src/lib.rs:7:46: 10:14]>>`
So, what am I missing? How can this work?
You can't do that directly because you would need a different type at each loop iteration: Chain<FilterMap, FilterMap>
the first time, then Chain<Chain<FilterMap, FilterMap>, FilterMap>
the second time, then Chain<Chain<Chain<FilterMap, FilterMap>, FilterMap>, FilterMap>
the third time, and so on.
This issue can be solved by boxing the iterators:
pub fn convert(s: String, num_rows: i32) -> String {
let mut iter: Box<dyn Iterator<Item=char>> = Box::new (s.chars().enumerate().filter_map(|(i, c)| {
if i % ((num_rows as usize - 1)*2) == 0 {Some(c)} else {None}}));
for n in 1..num_rows as usize {
iter = Box::new(iter.chain(
s.chars().enumerate().filter_map(|(i,c)| {
let cycle = (num_rows as usize - 1)*2;
if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
}))
);
}
iter.collect()
}
Which gives you a lifetime error about n
being borrowed by the closure, easily fixed by adding move
:
pub fn convert(s: String, num_rows: i32) -> String {
let mut iter: Box<dyn Iterator<Item=char>> = Box::new (s.chars().enumerate().filter_map(|(i, c)| {
if i % ((num_rows as usize - 1)*2) == 0 {Some(c)} else {None}}));
for n in 1..num_rows as usize {
iter = Box::new(iter.chain(
s.chars().enumerate().filter_map(move |(i,c)| {
let cycle = (num_rows as usize - 1)*2;
if i % cycle == n || i % cycle == cycle - n {Some(c)} else {None}
}))
);
}
iter.collect()
}