Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone last element from vector?

Tags:

rust

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?

like image 845
Remagpie Avatar asked Dec 20 '22 01:12

Remagpie


1 Answers

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()
like image 163
Veedrac Avatar answered Jan 20 '23 10:01

Veedrac