首页 文章

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

提问于
浏览
5

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

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通过使用引用或某种借用来停止移动值 . 在这种情况下,我 could 将我的方法签名更改为借用,但对于某些库,您并不总是能够这样做 . (我想在这种情况下处理hyper ......)

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

2 回答

  • 0

    为什么?

    当你做元组时

    (p.choice, p.age)
    

    memcpy 来自 Person p.choicep.age .

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

    p.choices 的类型为 Choices ,这是 not Copy . 这意味着 memcpy 被视为"move",因此旧值不可用 . 这意味着 p 处于无效状态,因此您无法在其上调用 announce .

    解决方案#1

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

    如果你只能安全地制作 Choices Clone ,那么你必须在 match 中使用 clone .

    解决方案#2

    你可以参考 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) 时的移动将失败 - 移动将使活动的借用变量无效 .

    注意事项

    announce 没有理由让 announce 消耗 Person - 它只需要看一下它 . 只有在小型 Copy 类型的情况下,才能获取参考值 .

    请注意,让 announce 获取引用意味着 match 也可以保留 p 中的引用,这使得它可以更广泛地应用 .

    to_string 主要用于非字符串对象 . intoto_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);
    }
    
  • 12

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

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

    p.age doesn 't need that only because it' s原始类型和基元类型实现Copy trait

    但Choices没有实现复制特性,因此它们会在匹配中移动 . 当你致电 announce() 时,这会导致他们无法使用

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

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

    I have not enough knowledge of Rust to truly explain why it works, so if you can add something useful, please do

相关问题