当借用泛型类型时,无法移动被借用内容

5
我有一个程序,大致看起来像这样:
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);
}

然后我遇到了以下错误:

错误:无法移动借用内容

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

我不完全确定为什么会出现这种情况。有人能解释一下,为什么在调用 get_firstTest<i32> 没有被借用,而 Test<T> 却被借用了吗?

1个回答

10
简短的回答是,i32 实现了 Copy 特性,但 T 没有。如果你使用 fn generic_main<T: Copy>(t: Test<T>),那么你的问题就得到了解决。
更详细的解释是,Copy 是一个特殊的特性,它意味着值可以通过简单地复制位来复制。像 i32 这样的类型实现了 Copy。像 String 这样的类型不实现 Copy,因为它需要堆分配。如果你只是通过复制位来复制一个 String,你最终会得到两个指向同一块内存的 String 值。这是不好的(不安全!)。
因此,给你的 T 添加 Copy 限定相当严格。一个更不严格的限定是 T: CloneClone 特性类似于 Copy(因为它也复制值),但通常不仅仅是“复制位”。例如,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的更多信息。文档中有一些不错的示例,包括如何为自己的类型实现Copy

“移动位”这个术语有点令人困惑,因为 Rust 通过移动位来进行移动。也许使用“复制位”会更清晰明了? - Matthieu M.
@MatthieuM。好的。已更新。就我所知,我直接从语言参考中引用了先前的措辞:“对于可以通过简单移动位来复制的“普通旧数据”类型进行复制。” - BurntSushi5
@BurntSushi5:有趣,也许我们应该警告Steve,在“复制”的上下文中使用“移动”有点令人困惑? - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接