在模式匹配期间防止移动语义

时间:2015-05-31 21:40:15

标签: pattern-matching rust move

我在这里有一个愚蠢的例子,只是为了演示我遇到的另一个库和模式匹配的问题。

struct Person {
    name: String,
    age: i32,
    choice: Choices
}

#[derive(Debug)]
enum Choices {
    Good,
    Neutral,
    Evil
}

fn find(p: Person) {
    match (p.choice, p.age) {
        (Choices::Good, a) if a < 80 => {
            announce(p);
        }
        (_, a) if a >= 80 => {
            println!("You're too old to care.");
        }
        _ => {
            println!("You're not very nice!")
        }
    }
}

fn announce(p: Person) {
    println!("Your name is {}. You are {:?}.", p.name, p.choice);
}

fn main() {
    let p = Person {
                name: "Bob".to_string(),
                age: 20,
                choice: Choices::Good
            };
    find(p);
}

现在问题似乎是在模式匹配期间,移动语义将启动并取代我的Person中的内部结构(Thing)的所有权。

当我将这个人移到下一个方法时,我不能因为它被部分移动了。

Compiling match v0.1.0 (file:///home/jocull/Documents/Projects/Rust/learn/match)
src/main.rs:17:13: 17:14 error: use of partially moved value: `p`
src/main.rs:17          announce(p);
                                 ^
src/main.rs:15:9: 15:17 note: `p.choice` moved here because it has type `Choices`, which is non-copyable
src/main.rs:15  match (p.choice, p.age) {
                       ^~~~~~~~
error: aborting due to previous error
Could not compile `match`.

我的直觉说我需要让Rust通过使用引用或某种借用来停止移动值。在这种情况下,我可以将我的方法签名更改为借用,但对于某些库,您并不总是能够这样做。 (我想在这种情况下处理hyper ...)

有没有办法让match在匹配过程中使用引用而不是移动值?谢谢!

2 个答案:

答案 0 :(得分:12)

为什么?

制作元组时

(p.choice, p.age)

memcpyp.choicep.age的{​​{1}}。

可以为Person执行此操作,因为它是p.age类型 - 您可以在Copy之后继续使用旧值。

memcpy类型为p.choices Choices。这意味着Copy被视为&#34;移动&#34;,因此旧值不可用。这意味着memcpy处于无效状态,因此您无法在其上调用p

解决方案#1

由于announce是一个微不足道的Choices,您只能enum。这意味着您可以继续使用旧的#[derive(Copy, Clone)]

如果您只能安全地制作p.choices Choices,那么您必须Clone代替clone

解决方案#2

您可以通过引用

获取match
p.choices

这只能起作用,因为match (&p.choice, p.age) { (&Choices::Good, a) if a < 80 => { announce(p); } ... } 是完全匹配的,因此可以放弃借用。如果你有了

&Choices::Good

借用仍然是活跃的,因此调用match (&p.choice, p.age) { (&x, a) if a < 80 => { announce(p); } ... } 时的移动将失败 - 移动将使活动的借用变量无效。

注释

你在这里做了大量的事情 - 传递一些参考资料要灵活得多!没有理由announce(p) 消费 a announce - 它只需要稍微查看一下。只有小Person类型时,才建议您在参考时使用值。

请注意,让Copy获取引用意味着announce也可以保留match内的引用,这使其更广泛适用。

p主要用于非字符串对象。 <{1}}和to_string速度更快,into也更短。

to_owned

答案 1 :(得分:0)

所以我再次尝试新的代码更改:)

在您当前的代码中,如果您使用借用而不是移动匹配它是有效的。

p.age并不仅仅因为它是原始类型而原始类型实现了Copy trait

但Choices没有实现复制特性,所以他们在比赛中被移动了。当您拨打announce()

时,这会导致它们无法使用
match (&p.choice, p.age) {
        (&Choices::Good, a) if a < 80 => {
            announce(p);
        }
        ...
}

它可以解决有关部分移动的错误。我想这是因为你在比赛中选择了。但选择是人的一部分,所以它部分移动。

我对Rust没有足够的知识来真正解释它的工作原理,所以如果你能添加一些有用的东西,请