Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot move out of borrowed content when borrowing a generic type

Tags:

pointers

rust

I have a program that more or less looks like this

struct Test<T> {
    vec: Vec<T>
}

impl<T> Test<T> {
    fn get_first(&self) -> &T {
        &self.vec[0]
    }

    fn do_something_with_x(&self, x: T) {
        // Irrelevant
    }
}

fn main() {
    let t = Test { vec: vec![1i32, 2, 3] };
    let x = t.get_first();
    t.do_something_with_x(*x);
}

Basically, we call a method on the struct Test that borrows some value. Then we call another method on the same struct, passing the previously obtained value.

This example works perfectly fine. Now, when we make the content of main generic, it doesn't work anymore.

fn generic_main<T>(t: Test<T>) {
    let x = t.get_first();
    t.do_something_with_x(*x);
}

Then I get the following error:

error: cannot move out of borrowed content

src/main.rs:14 let raw_x = *x;

I'm not completely sure why this is happening. Can someone explain to me why Test<i32> isn't borrowed when calling get_first while Test<T> is?

like image 563
IluTov Avatar asked Feb 18 '15 22:02

IluTov


1 Answers

The short answer is that i32 implements the Copy trait, but T does not. If you use fn generic_main<T: Copy>(t: Test<T>), then your immediate problem is fixed.

The longer answer is that Copy is a special trait which means values can be copied by simply copying bits. Types like i32 implement Copy. Types like String do not implement Copy because, for example, it requires a heap allocation. If you copied a String just by copying bits, you'd end up with two String values pointing to the same chunk of memory. That would not be good (it's unsafe!).

Therefore, giving your T a Copy bound is quite restrictive. A less restrictive bound would be T: Clone. The Clone trait is similar to Copy (in that it copies values), but it's usually done by more than just "copying bits." For example, the String type will implement Clone by creating a new heap allocation for the underlying memory.

This requires you to change how your generic_main is written:

fn generic_main<T: Clone>(t: Test<T>) {
    let x = t.get_first();
    t.do_something_with_x(x.clone());
}

Alternatively, if you don't want to have either the Clone or Copy bounds, then you could change your do_something_with_x method to take a reference to T rather than an owned T:

impl<T> Test<T> {
    // other methods elided

    fn do_something_with_x(&self, x: &T) {
        // Irrelevant
    }
}

And your generic_main stays mostly the same, except you don't dereference x:

fn generic_main<T>(t: Test<T>) {
    let x = t.get_first();
    t.do_something_with_x(x);
}

You can read more about Copy in the docs. There are some nice examples, including how to implement Copy for your own types.

like image 126
BurntSushi5 Avatar answered Nov 05 '22 23:11

BurntSushi5