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

joc*_*ull 7 move pattern-matching rust

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

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);
}
Run Code Online (Sandbox Code Playgroud)

现在问题似乎是在模式匹配期间,移动语义将启动并获取我的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`.
Run Code Online (Sandbox Code Playgroud)

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

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

Vee*_*rac 16

为什么?

当你做元组时

(p.choice, p.age)
Run Code Online (Sandbox Code Playgroud)

你们memcpyp.choicep.age你们Person.

这样做p.age是可以的,因为它是一种Copy类型 - 您可以继续使用旧值memcpy.

p.choicesChoices不是 的类型Copy.这意味着它memcpy被视为"移动",因此旧值不可用.这意味着p处于无效状态,因此您无法调用announce它.

解决方案#1

既然Choices是微不足道的enum,你可以#[derive(Copy, Clone)].这意味着您可以继续使用旧版本p.choices.

如果你只能安全地制造Choices Clone,那么你必须clonematch相反的地方.

解决方案#2

您可以p.choices通过参考:

match (&p.choice, p.age) {
    (&Choices::Good, a) if a < 80 => {
        announce(p);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

这只能起作用,因为&Choices::Good它是完全匹配的,所以可以放弃借用.如果你有的话

match (&p.choice, p.age) {
    (&x, a) if a < 80 => {
        announce(p);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

借用仍然是活跃的,因此调用时的移动announce(p)将失败 - 移动将使活动的借入变量无效.

笔记

你在这里做了很多事情 - 传递一些参考资料要灵活得多!有没有理由announce消费一个Person-它只是需要看它一会儿.当您可以参考时按值获取仅适用于小型Copy.

请注意,announce采用引用意味着match允许也允许内部引用p,这使其更广泛适用.

to_string主要用于非字符串对象.into而且to_owned速度更快,into也更短.

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

#[derive(Copy, Clone, 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".into(),
        age: 20,
        choice: Choices::Good
    };

    find(&p);
}
Run Code Online (Sandbox Code Playgroud)