在模式匹配期间防止移动语义
Preventing move semantics during 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 中内部结构(事物)的所有权。
当我要将此人移到下一个方法时,我不能,因为它已被部分移动。
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
在匹配过程中使用引用而不是移动值?谢谢!
所以我再次尝试更改新代码:)
在您当前的代码中,如果您在匹配中使用借用而不是移动,它就可以工作。
p.age
不需要它只是因为它是一个原始类型并且原始类型实现了 Copy
trait
但是 Choices 没有实现复制特性,所以它们在匹配中被移动了。这导致它们在您调用 announce()
时不可用
match (&p.choice, p.age) {
(&Choices::Good, a) if a < 80 => {
announce(p);
}
...
}
解决了部分移动的错误。我想这是因为你在比赛中移动了选择。但是 choice 是 Person 的一部分,所以它被部分移动了。
我对 Rust 的了解还不够,无法真正解释它的工作原理,所以如果你能添加一些有用的东西,请做
为什么?
创建元组时
(p.choice, p.age)
你 memcpy
p.choice
和 p.age
来自你的 Person
.
可以为 p.age
执行此操作,因为它是 Copy
类型 - 您可以在 memcpy
后继续使用旧值。
p.choices
是 Choices
类型, 不是 Copy
。这意味着 memcpy
被视为 "move",因此旧值不可用。这意味着 p
处于无效状态,因此您不能对其调用 announce
。
解决方案 #1
因为 Choices
是一个微不足道的 enum
,你可以 #[derive(Copy, Clone)]
。这意味着您可以继续使用旧的 p.choices
.
如果您只能安全地制作 Choices
Clone
,那么您必须 clone
改为 match
。
解决方案 #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
没有理由 消费 一个 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);
}
我这里有一个愚蠢的例子,只是为了演示我 运行 遇到另一个库和模式匹配的问题。
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 中内部结构(事物)的所有权。
当我要将此人移到下一个方法时,我不能,因为它已被部分移动。
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
在匹配过程中使用引用而不是移动值?谢谢!
所以我再次尝试更改新代码:)
在您当前的代码中,如果您在匹配中使用借用而不是移动,它就可以工作。
p.age
不需要它只是因为它是一个原始类型并且原始类型实现了 Copy
trait
但是 Choices 没有实现复制特性,所以它们在匹配中被移动了。这导致它们在您调用 announce()
match (&p.choice, p.age) {
(&Choices::Good, a) if a < 80 => {
announce(p);
}
...
}
解决了部分移动的错误。我想这是因为你在比赛中移动了选择。但是 choice 是 Person 的一部分,所以它被部分移动了。
我对 Rust 的了解还不够,无法真正解释它的工作原理,所以如果你能添加一些有用的东西,请做
为什么?
创建元组时
(p.choice, p.age)
你 memcpy
p.choice
和 p.age
来自你的 Person
.
可以为 p.age
执行此操作,因为它是 Copy
类型 - 您可以在 memcpy
后继续使用旧值。
p.choices
是 Choices
类型, 不是 Copy
。这意味着 memcpy
被视为 "move",因此旧值不可用。这意味着 p
处于无效状态,因此您不能对其调用 announce
。
解决方案 #1
因为 Choices
是一个微不足道的 enum
,你可以 #[derive(Copy, Clone)]
。这意味着您可以继续使用旧的 p.choices
.
如果您只能安全地制作 Choices
Clone
,那么您必须 clone
改为 match
。
解决方案 #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
没有理由 消费 一个 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);
}