借用泛型时无法移出借用的内容

Cannot move out of borrowed content when borrowing a generic type

我有一个大致像这样的程序

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);
}

基本上,我们在结构 Test 上调用一个借用一些值的方法。然后我们在同一个结构上调用另一个方法,传递之前获得的值。

这个例子工作得很好。现在,当我们将 main 的内容设为通用时,它就不再起作用了。

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

然后我得到以下错误:

error: cannot move out of borrowed content

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

我不完全确定为什么会这样。有人可以向我解释为什么在调用 get_first 时没有借用 Test<i32>Test<T> 是借用的吗?

简短的回答是 i32 实现了 Copy 特性,但 T 没有。如果您使用 fn generic_main<T: Copy>(t: Test<T>),那么您眼前的问题就解决了。

较长的答案是 Copy 是一个特殊特征,这意味着可以通过简单地复制位来复制值。 i32 等类型实现了 Copy。像 String 这样的类型 而不是 实现 Copy 因为,例如,它需要堆分配。如果你只是通过复制位来复制 String,你最终会得到两个指向同一内存块的 String 值。那可不好(不安全!)。

因此,给您的 T 一个 Copy 限制是非常严格的。限制较少的界限是 T: CloneClone trait 类似于 Copy(因为它复制值),但它通常不仅仅由 "copying bits." 完成,例如,String 类型将实现 Clone 通过为底层内存创建新的堆分配。

这需要您更改 generic_main 的书写方式:

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

或者,如果您不想使用 CloneCopy 边界,则可以更改 do_something_with_x 方法以采用 参考T 而不是拥有的 T:

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

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

你的 generic_main 基本保持不变,除了你不取消引用 x:

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

您可以阅读有关 Copy in the docs 的更多信息。有一些很好的示例,包括如何为您自己的类型实现 Copy