Sandeep Datta
Sandeep Datta

Reputation: 29345

Can a value be moved by taking a mutable reference?

Playpen

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

Answers (1)

Shepmaster
Shepmaster

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

Related Questions