I am currently learning Rust and came across a question: "How could I have a function that takes a collection and returns an iterator of its elements grouped by a given number?"
This is what I mean:
let v = vec![1, 2, 3, 4, 5, 6, 7, 8];
// `result` should be an iterator with
// the elements [1,2,3], [4,5,6], [7,8]
let result = myfunction(&v, 3); // groups of three (while possible)
This is easy when I implement the function with elements "aligned left" (we always fill the groups from the left to the right. If a group is missing one element, it will be the rightmost one).
However, to make a version of this function "aligned right", I have to know the amount of elements the iterator has. Here I experimented a bit:
#[allow(unused_variables)]
fn iter_from_iter<T: IntoIterator>(data: T) -> T::IntoIter
{
let it = data.into_iter();
it
}
fn main() {
let v = vec![1,2,3,4,5,6,7,8];
let it = (&v).into_iter(); // doesnt take ownership from `v`
let c = (&v).into_iter().count(); // here neither
println!("Iterator (returned): {:?}",
iter_from_iter(&v) // need to pass by reference, otherwise the ownership is moved
);
println!("Vector: {:?}", v);
println!("Iterator: {:?}", it);
println!("Count: {:?}", c);
}
/*
* output:
* Iterator (returned): Iter([1, 2, 3, 4, 5, 6, 7, 8])
* Vector: [1, 2, 3, 4, 5, 6, 7, 8]
* Iterator: Iter([1, 2, 3, 4, 5, 6, 7, 8])
* Count: 7
*/
I can even add the following lines to main():
let slice = "hello, world";
let chars = slice.chars();
let c = slice.chars().count();
println!("Chars Iterator: {:?}", chars);
println!("Chars Iterator (returned): {:?}", iter_from_iter(chars));
println!("Count: {:?}", c);
/*
* output:
* Chars Iterator (returned): Chars(['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'])
* Chars Iterator: Chars(['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'])
* Count: 12
*/
As we can see, the function iter_from_iter() can make an iterator from our argument. But if we change the function iter_from_iter() definition to the following
fn iter_from_iter<T: IntoIterator>(data: T) -> (T::IntoIter, usize)
{
let it = data.into_iter();
let count = data.into_iter().count();
(it, count)
}
The compiler complains
Compiling my-test v0.1.0 (/home/arch/codigos/rust/my-test)
error[E0382]: use of moved value: `data`
--> src/main.rs:5:17
|
2 | fn iter_from_iter<T: IntoIterator>(data: T) -> (T::IntoIter, usize)
| ---- move occurs because `data` has type `T`, which does not implement the `Copy` trait
3 | {
4 | let it = data.into_iter();
| ----------- `data` moved due to this method call
5 | let count = data.into_iter().count();
| ^^^^ value used here after move
|
note: this function takes ownership of the receiver `self`, which moves `data`
--> /home/arch/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:233:18
|
233 | fn into_iter(self) -> Self::IntoIter;
| ^^^^
help: consider further restricting this bound
|
2 | fn iter_from_iter<T: IntoIterator + Copy>(data: T) -> (T::IntoIter, usize)
| ^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
error: could not compile `my-test`
To learn more, run the command again with --verbose.
If I make the suggested changes (require T to implement the Copy trait), the vector case now compiles, but if I insert those lines about Chars again, the code doesn't compile, because
the trait
Copyis not implemented byChars<'_>
If I then remove the Copy trait bound and change the function signature to fn iter_from_iter<T: IntoIterator>(data: &T) -> (T::IntoIter, usize), the compiler complains again
Compiling my-test v0.1.0 (/home/arch/codigos/rust/my-test)
error[E0507]: cannot move out of `*data` which is behind a shared reference
--> src/main.rs:4:14
|
4 | let it = data.into_iter();
| ^^^^ move occurs because `*data` has type `T`, which does not implement the `Copy` trait
error[E0507]: cannot move out of `*data` which is behind a shared reference
--> src/main.rs:5:17
|
5 | let count = data.into_iter().count();
| ^^^^ move occurs because `*data` has type `T`, which does not implement the `Copy` trait
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0507`.
error: could not compile `my-test`
To learn more, run the command again with --verbose.
I then tried some things like using (&data).into_iter() instead of data.into_iter(), but nothing seems to solve my problem...
I searched on the internet a lot about Iterators and IntoIterators (including the docs), but couldn't find a solution. It's been days now.
Could someone more experienced, please, tell me what is needed for:
- Taking an
IntoIteratoras argument; - not taking the ownership of it;
- getting the result from its
into_iter()inside my function; - and still being able to call
into_iter().count()inside my function.
So that I could have something like this (here I use ellipsis where I don't know what should be the right type):
// make groups of `size` elements from an IntoIterator
fn <...>myfunction(data: ..., size: usize) -> ... {
let iter = data.into_iter();
let len = data.into_iter().count(); // `iter` must still be valid
// -- gets elements from `iter` and do the rest of operations --
}
Hope you can help me. Thank you in advance!
Obs.: I thought about making T::IntoIter have the ExactSizeIterator so that I could use the function len() on the Iterator obtained from data.into_iterator(), but it doesn't work either. That's because Chars doesn't implement it...