5

I'm trying to write code that gets the last element of some vector and do different actions (including mutation of the vector) depending on that element.

I tried like this:

#[derive(Clone, PartialEq)]
enum ParseItem {
    Start,
    End,
}

let mut item_vec = vec![ParseItem::End];
loop {
    let last_item = *item_vec.last().clone().unwrap();
    match last_item {
        ParseItem::End => item_vec.push(ParseItem::Start),
        _ => break,
    }
}

And I get the following error:

error: cannot move out of borrowed content
let last_item = *item_vec.last().clone().unwrap();

I thought by cloning item_vec.last(), the problems with ownership would be solved, but it seems not.

If I try the same thing with a vector of integers like this:

let mut int_vec = vec![0];
loop {
    let last_int = *int_vec.last().clone().unwrap();
    match last_int {
        0 => int_vec.push(1),
        _ => break,
    }
}

the compiler doesn't complain about borrowing.

Why does my code fails to compile?

Remagpie
  • 685
  • 6
  • 15

1 Answers1

11

item_vec.last() is an Option<&T>.

item_vec.last().clone() is another Option<&T>. This actually performs a shallow copy of the reference. This means you haven't actually fixed anything!

Intuitively, this makes sense - cloning a pointer can return a value type to store directly on the stack, but a clone of an Option<&T> can't clone the T because it has nowhere to put it.

This works because an Option<T> actually calls clone on an &T, so Option<&T> calls clone on an &&T, which means the &self parameter in the trait resolves to self = &T. This means we use the impl of Clone for &T:

impl<'a, T: ?Sized> Clone for &'a T {
    /// Returns a shallow copy of the reference.
    #[inline]
    fn clone(&self) -> &'a T { *self }
}

*item_vec.last().clone().unwrap() thus is still a borrow of the vector.

One can fix this in two basic ways. One is to use Option's cloned method, which clones the inner reference away:

item_vec.last().cloned().unwrap()

This is implemented as a map on the internal data:

impl<'a, T: Clone> Option<&'a T> {
    /// Maps an Option<&T> to an Option<T> by cloning the contents of the Option.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn cloned(self) -> Option<T> {
        self.map(|t| t.clone())
    }
}

The other option is to unwrap and only then clone the reference, to get a value out:

item_vec.last().unwrap().clone()
Veedrac
  • 58,273
  • 15
  • 112
  • 169
  • Thank you for your explanation! But I still have some questions. 1. first solution with cloned() works well, but second solution still has same error. Why does this happens? 2. Why does the integer version of code works without all these corrections? – Remagpie Aug 09 '15 at 21:25
  • @Yang The second solution accidentally had an extra "`*`"; try again to see if it works now. The integer version works because integers are `Copy`, which means assignment `let new = *ref` causes a copy, not a move. [I've talked more about moves vs. copies elsewhere.](http://stackoverflow.com/a/31171431/1763356) – Veedrac Aug 10 '15 at 10:22