在模式匹配期间防止移动语义 [英] Preventing move semantics during pattern matching
问题描述
我在这里有一个愚蠢的示例,只是为了演示我在另一个库和模式匹配中遇到的问题.
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)
您memcpy
和p.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 memcpy
ing 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
主要用于非字符串对象. into
和to_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屋!