[英]Understanding move semantics for enums Rust
So I'm looking at the impl for is_some()
for Option
and I noticed that it uses match *self {}
under the hood...so it moves it internally. 所以我在看
Option
is_some()
,我注意到它在is_some()
使用match *self {}
...因此它在内部移动它。
My question is, if it gets moved, how am I able to do something like this? 我的问题是,如果它移动了,我怎么能做这样的事情? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f094da12290b77bad526674467e51043
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f094da12290b77bad526674467e51043
fn main() {
let x = Option::Some(3);
x.is_some();
x.is_some();
}
My expectation is that I should be able to call is_some()
only once, and the next time I call it I should get some sort of error saying it's been moved...but no, it all compiles fine. 我的期望是,我只能调用一次
is_some()
,而下次调用它时,我应该会收到一些错误消息,说它已经被移动了……但是,所有的编译都很好。
What am I misunderstanding? 我有什么误会?
*self
in match *self { ... }
does not move (or copy) what self
points to. *self
in match *self { ... }
不会移动(或复制) self
指向的内容。 From " The Rust Reference " (emphasis mine), 从“ The Rust Reference ”(重点是我的),
A
match
behaves differently depending on whether or not the scrutinee expression is a place expression or value expression.match
行为取决于scrutinee表达式是place表达式还是value表达式。 If the scrutinee expression is a value expression, it is first evaluated into a temporary location, ...如果scrutinee表达式是一个值表达式,则首先将其求值到一个临时位置, ...
When the scrutinee expression is a place expression, the match does not allocate a temporary location;
当scrutinee表达式是place表达式时,匹配项不会分配临时位置; however, a by-value binding may copy or move from the memory location.
但是,按值绑定可以从内存位置复制或移动。 ...
...
*self
is a place expression. *self
是一个地方表达。 From " The Rust Reference " (emphasis mine), 从“ The Rust Reference ”(重点是我的),
Expressions are divided into two main categories: place expressions and value expressions.
表达式分为两个主要类别:位置表达式和值表达式。 ...
...
A place expression is an expression that represents a memory location.
位置表达式是代表存储位置的表达式。 These expressions are paths which refer to local variables, static variables, dereferences (
*expr
) , array indexing expressions (expr[expr]
), field references (expr.f
) and parenthesized place expressions.这些表达式是引用局部变量,静态变量, 取消引用(
*expr
) ,数组索引表达式(expr[expr]
),字段引用(expr.f
)和带括号的位置表达式的expr.f
。 All other expressions are value expressions.所有其他表达式都是值表达式。
A value expression is an expression that represents an actual value.
值表达式是表示实际值的表达式。
You might also be interested to know that the Some(_) => true
arm in the match
body does not bind anything. 您可能还想知道
match
正文中的Some(_) => true
手臂没有任何约束。 From " The Rust Reference ", 从“ The Rust Reference ”中,
Unlike identifier patterns, it does not copy, move or borrow the value it matches.
与标识符模式不同,它不会复制,移动或借用与其匹配的值。
where "it" means the wildcard pattern ( _
). 其中“ it”表示通配符模式(
_
)。
(See the @dkim's answer for a more official and cited answer) (有关更多官方和引用的答案,请参见@dkim的答案)
If the signature of the function accepts by reference, it does not take ownership of the value. 如果函数的签名通过引用接受,则它不拥有该值的所有权。 Option.is_some() actually takes &self not self.
Option.is_some()实际上需要&self而不是self。
The interesting part is how *self
is allowed to be used in a function that receives &self
when Self: Copy
is not bounded. 有趣的部分是当
Self: Copy
不受限制时,如何允许*self
在接收&self
的函数中使用。
To test this, let's create a minimal example containing something similar: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d7b137b74b5cd8f8bb57398ae01bf4e3 为了测试这一点,让我们创建一个包含类似内容的最小示例: https : //play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d7b137b74b5cd8f8bb57398ae01bf4e3
#[derive(Debug)]
pub enum A {
X(String),
Y(i32),
}
pub fn f(a: &A) {
match *a {
A::X(_) => {
// dbg!(s);
}
A::Y(_i) => {
// dbg!(i);
}
};
}
This compiles fine. 这样编译就可以了。 But let's change the
A::X(_)
pattern to A::X(_s)
: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=93030e5c3f84532532c5db966c798bd6 但是让我们将
A::X(_)
模式更改为A::X(_s)
: https : A::X(_s)
? A::X(_s)
= A::X(_s)
& A::X(_s)
= A::X(_s)
& A::X(_s)
= A::X(_s)
& A::X(_s)
= A::X(_s)
#[derive(Debug)]
pub enum A {
X(String),
Y(i32),
}
pub fn f(a: &A) {
match *a {
A::X(_s) => {
// dbg!(s);
}
A::Y(_i) => {
// dbg!(i);
}
};
}
This fails to compile: 无法编译:
error[E0507]: cannot move out of `a.0` which is behind a shared reference
--> src/lib.rs:7:11
|
7 | match *a {
| ^^ help: consider borrowing here: `&*a`
8 | A::X(_s) => {
| --
| |
| data moved here
| move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait
So it appears that dereferencing a non-Copy enum is perfectly fine, as long as it wouldn't be used for moving inner non-Copy values. 因此,似乎取消引用非Copy枚举是完全可以的,只要它不用于移动内部非Copy值即可。
_
is fine because it guarantees that the underlying value would never be used, while _s
does not compile because it is just a normal allow(unused) variable. _
很好,因为它可以保证永远不会使用基础值,而_s
则不能编译,因为它只是一个普通的allow(未使用)变量。
This also makes sense because it allows the same match arm to work on both Copy and non-Copy types, as long as no usages violate the ownership rules 这也是有道理的,因为只要没有用法违反所有权规则,它允许相同的匹配臂同时作用于复制和非复制类型
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.