Reputation: 29345
pub trait Skip<I: Iterator> {
fn skip(&mut self, steps: usize);
}
impl<I: Iterator> Skip<I> for I {
fn skip(&mut self, mut steps: usize) {
for _ in self {
steps -= 1;
if steps <= 0 {
break;
}
}
}
}
fn main() {
let s = "abc123def";
let mut chars = s.chars();
chars.skip(2);
println!("{:?}", chars.collect::<String>());
}
Error:
error: use of moved value: `chars` [--explain E0382]
--> <anon>:20:22
19 |> chars.skip(2);
|> ----- value moved here
20 |> println!("{:?}", chars.collect::<String>());
|> ^^^^^ value used here after move
<anon>:20:22: 20:27: note: in this expansion of format_args!
<anon>:20:22: 20:27: note: in this expansion of print! (defined in <std macros>)
<anon>:20:22: 20:27: note: in this expansion of println! (defined in <std macros>)
note: move occurs because `chars` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait
error: aborting due to previous error
playpen: application terminated with error code 101
Upvotes: 4
Views: 1579
Reputation: 431949
Can a value be moved by taking a mutable reference?
No. References do not move the referred-to items. That's one large point of references.
However, Iterator::skip
does take ownership of the iterator, which is a move:
fn skip(self, n: usize) -> Skip<Self>
To solve the error, you can... take a mutable reference!
chars.by_ref().skip(2);
However, note that calling an iterator adapter without using it does nothing:
warning: unused result which must be used: iterator adaptors are lazy and do nothing unless consumed
The real problem here is that you aren't calling the skip
you think you are. If you rename your method to skip2
, you will see that it works as intended.
This is because method lookup prefers methods that accept the receiver by-value (self
) before by-reference (&self
) before by-mutable-reference (&mut self
).
It's probably a bad idea to pick the same method name as the standard library, especially for iterators, but it's very surprising that a warning isn't printed saying there are multiple applicable methods in scope.
You could use Universal Function Call Syntax (UFCS) to specify which implementation to call though:
pub trait Skip: Iterator {
fn skip(&mut self, steps: usize) {
for _ in self.take(steps) {}
}
}
impl<I> Skip for I where I: Iterator {}
fn main() {
let s = "abc123def";
let mut chars = s.chars();
Skip::skip(&mut chars, 2);
println!("{:?}", chars.collect::<String>());
}
Upvotes: 5