在模式匹配期间防止移动语义 [英] Preventing move semantics during pattern matching

查看:79
本文介绍了在模式匹配期间防止移动语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a silly example here, just to demonstrate an issue I'm running into with another library and pattern matching.

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内的内部结构(事物)的所有权.

Now the issue seems to be that during pattern matching, move semantics will kick in and take ownership over the inner struct (Thing) in my Person.

当我将人员转移到下一个方法时,我不能,因为它已被部分移动.

When I go to move the person on to the next method, I can't because it's been partially moved.

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停止使用引用或某种形式的借口来移动值.在这种情况下,我可以将方法签名更改为借用,但是对于某些库,您并非总是能够做到这一点. (在这种情况下,我正在尝试处理超级 ...)

My gut says I need to get Rust to stop moving the value by using a reference or borrow of some kind. In this case I could change my method signature to borrow, but with some libraries you aren't always able to do that. (I am trying to deal with hyper in this case...)

是否有一种方法可以使match在匹配期间使用引用而不是移动值?谢谢!

Is there a way to get the match to use references during matching instead of moving the values? Thank you!

推荐答案

为什么?

创建元组时

Why?

When you make the tuple

(p.choice, p.age)

memcpyp.age都来自Person.

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

It's OK to do this for p.age because it's a Copy type - you can continue using the old value after memcpying from it.

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

p.choices is of type Choices which is not Copy. This means that the memcpy is treated as a "move", so the old value is not usable. This means p is in an invalid state, so you can't call announce on it.

由于Choices是琐碎的enum,因此您只需#[derive(Copy, Clone)].这意味着您可以继续使用旧的p.choices.

Since Choices is a trivial enum, you can just #[derive(Copy, Clone)]. This means that you are allowed to continue using the old p.choices.

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

If you can only safely make Choices Clone, then you'd have to clone it in the match instead.

您可以通过引用获取p.choices:

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

这仅有效,因为&Choices::Good是完全匹配项,因此可以放弃借用.如果有的话

This only works because &Choices::Good is an exact match so the borrow can be relinquished. If you had instead

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

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

the borrow would still be active and so the move when calling announce(p) would fail - the move would invalidate an active borrowed variable.

您在这里做了很多工作-传递一些引用更加灵活! announce没有理由消费一个Person-它只需要看一点.仅当较小的Copy类型时,才建议您按值获取值.

You're doing an awful lot of moving here - passing a few references is a lot more flexible! There's no reason for announce to consume a Person - it just needs to look at it for a bit. Taking by value when you could take a reference is only advisable for small Copy types.

请注意,使用announce进行引用意味着match也可以保留在p内部的引用,这使其可以更广泛地应用.

Note that having announce take a reference means that the match is allowed to also be holding on to references inside p, which makes it more widely applicable.

to_string主要用于非字符串对象. intoto_owned更快,而into也更短.

to_string is mostly for use for non-string objects. into and to_owned are faster and into is also a lot shorter.

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

这篇关于在模式匹配期间防止移动语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆