如果我从不将MutexGuard分配给变量,它在哪里? [英] Where is a MutexGuard if I never assign it to a variable?
问题描述
我不了解内部代码块中MutexGuard
的位置".互斥锁被锁定和展开,产生MutexGuard
.这段代码设法以某种方式取消对MutexGuard
的引用,然后可变地借用该对象. MutexGuard
去哪了?同样,令人困惑的是,这种取消引用不能用deref_mut
代替.为什么?
I don't understand "where" the MutexGuard
in the inner block of code is. The mutex is locked and unwrapped, yielding a MutexGuard
. Somehow this code manages to dereference that MutexGuard
and then mutably borrow that object. Where did the MutexGuard
go? Also, confusingly, this dereference cannot be replaced with deref_mut
. Why?
use std::sync::Mutex;
fn main() {
let x = Mutex::new(Vec::new());
{
let y: &mut Vec<_> = &mut *x.lock().unwrap();
y.push(3);
println!("{:?}, {:?}", x, y);
}
let z = &mut *x.lock().unwrap();
println!("{:?}, {:?}", x, z);
}
推荐答案
Summary: because *x.lock().unwrap()
performs an implicit borrow of the operand x.lock().unwrap()
, the operand is treated as a place context. But since our actual operand is not a place expression, but a value expression, it gets assigned to an unnamed memory location (basically a hidden let
binding)!
有关详细说明,请参见下文.
See below for a more detailed explanation.
在深入探讨之前,请先讲两个重要术语. Rust中的表达式主要分为两类:位置表达式和值表达式.
Before we dive in, first two important terms. Expressions in Rust are divided into two main categories: place expressions and value expressions.
- 位置表达式表示具有主目录(存储位置)的值.例如,如果您有
let x = 3;
,则x
是一个位置表达式.从历史上讲,这就是左值表达式. - 值表达式表示没有主目录的值(我们只能使用该值,没有与之关联的内存位置).例如,如果您有
fn bar() -> i32
,则bar()
是一个值表达式.诸如3.14
或"hi"
的文字也是值表达式.从历史上讲,这些被称为 rvalue表达式.
- Place expressions represent a value that has a home (a memory location). For example, if you have
let x = 3;
thenx
is a place expression. Historically this was called lvalue expression. - Value expressions represent a value that does not have a home (we can only use the value, there is no memory location associated with it). For example, if you have
fn bar() -> i32
thenbar()
is a value expression. Literals like3.14
or"hi"
are value expressions too. Historically these were called rvalue expressions.
有一个很好的经验法则来检查某物是否是一个位置或值表达式:将其写在作业的左侧是否有意义?".如果匹配(例如my_variable = ...;
),则它是一个位置表达式;如果不匹配(例如3 = ...;
),则它是一个值表达式.
There is a good rule of thumb to check if something is a place or value expression: "does it make sense to write it on the left side of an assignment?". If it does (like my_variable = ...;
) it is a place expression, if it doesn't (like 3 = ...;
) it's a value expression.
还存在 place上下文和 value上下文.这些基本上是可以放置表达式的插槽".仅有几个 place上下文,(通常,请参见下文)需要一个 place表达式:
There also exist place contexts and value contexts. These are basically the "slots" in which expressions can be placed. There are only a few place contexts, which (usually, see below) require a place expression:
- (化合物)赋值表达式(
⟨place context⟩ = ...;
,⟨place context⟩ += ...;
)的左侧 - 借位表达式的运算符(
&⟨place context⟩
和&mut ⟨place context⟩
) - ...再加上一些
- Left side of a (compound) assignment expression (
⟨place context⟩ = ...;
,⟨place context⟩ += ...;
) - Operand of an borrow expression (
&⟨place context⟩
and&mut ⟨place context⟩
) - ... plus a few more
请注意,场所表达式严格来说更强大".它们可以在值上下文中使用而不会出现问题,因为它们也表示值.
Note that place expressions are strictly more "powerful". They can be used in a value context without a problem, because they also represent a value.
(参考中的相关章节)
让我们构建一个小的虚拟示例来演示Rust所做的事情:
Let's build a small dummy example to demonstrate a thing Rust does:
struct Foo(i32);
fn get_foo() -> Foo {
Foo(0)
}
let x: &Foo = &get_foo();
这行得通!
我们知道表达式get_foo()
是值表达式.而且我们知道借位表达式的操作数是 place context .那么为什么要编译呢? 地方上下文不需要地方表达式吗?
We know that the expression get_foo()
is a value expression. And we know that the operand of a borrow expression is a place context. So why does this compile? Didn't place contexts need place expressions?
Rust创建临时的let
绑定!来自参考:
Rust creates temporary let
bindings! From the reference:
在大多数场所表达式上下文中使用值表达式时,会创建一个临时的未命名存储位置,并将其初始化为该值,然后表达式求值到该位置.[...]
When using a value expression in most place expression contexts, a temporary unnamed memory location is created initialized to that value and the expression evaluates to that location instead [...].
所以上面的代码等效于:
So the above code is equivalent to:
let _compiler_generated = get_foo();
let x: &Foo = &_compiler_generated;
这就是使您的Mutex
示例起作用的原因:MutexLock
被分配给一个临时的未命名存储位置!那就是它的住所.让我们看看:
This is what makes your Mutex
example work: the MutexLock
is assigned to a temporary unnamed memory location! That's where it lives. Let's see:
&mut *x.lock().unwrap();
x.lock().unwrap()
部分是一个值表达式:它的类型为MutexLock
,由函数(unwrap()
)返回,就像上面的get_foo()
一样.然后只剩下最后一个问题:deref *
运算符的操作数是否是位置上下文?我没有在上面的地方比赛列表中提及它.
The x.lock().unwrap()
part is a value expression: it has the type MutexLock
and is returned by a function (unwrap()
) just like get_foo()
above. Then there is only one last question left: is the operand of the deref *
operator a place context? I didn't mention it in the list of place contests above...
难题中的最后一块是隐性借用.来自参考:
The last piece in the puzzle are implicit borrows. From the reference:
某些表达式将通过隐式借用它来将其视为位置表达式.
Certain expressions will treat an expression as a place expression by implicitly borrowing it.
其中包括解引用运算符(*
)的操作数"!而且任何隐式借用的所有操作数都是场所上下文!
These include "the operand of the dereference operator (*
)"! And all operands of any implicit borrow are place contexts!
因此,由于*x.lock().unwrap()
执行隐式借用,因此操作数x.lock().unwrap()
是位置上下文,但是由于我们的实际操作数不是位置而是值表达式,因此将其分配给未命名的存储位置!
So because *x.lock().unwrap()
performs an implicit borrow, the operand x.lock().unwrap()
is a place context, but since our actual operand is not a place, but a value expression, it gets assigned to an unnamed memory location!
临时寿命"有一个重要的细节.让我们再次看一下报价:
There is an important detail of "temporary lifetimes". Let's look at the quote again:
在大多数位置表达式上下文中使用值表达式时,会创建一个临时的未命名存储位置,并将其初始化为该值,然后表达式求值到该位置.[...]
When using a value expression in most place expression contexts, a temporary unnamed memory location is created initialized to that value and the expression evaluates to that location instead [...].
根据情况,Rust选择具有不同生命周期的内存位置!在上面的&get_foo()
示例中,临时的未命名存储位置的寿命为封闭块的寿命.这等效于我上面显示的隐藏的let
绑定.
Depending on the situation, Rust chooses memory locations with different lifetimes! In the &get_foo()
example above, the temporary unnamed memory location had a lifetime of the enclosing block. This is equivalent to the hidden let
binding I showed above.
但是,此临时未命名的内存位置"并不总是等同于let
绑定!让我们看一下这种情况:
However, this "temporary unnamed memory location" is not always equivalent to a let
binding! Let's take a look at this case:
fn takes_foo_ref(_: &Foo) {}
takes_foo_ref(&get_foo());
在这里,Foo
值仅在takes_foo_ref
调用期间有效,并且不再有效!
Here, the Foo
value only lives for the duration of the takes_foo_ref
call and not longer!
通常,如果将对临时引用的引用用作函数调用的参数,则临时仅存在于该函数调用中.这也包括&self
(和&mut self
)参数.因此,在get_foo().deref_mut()
中,Foo
对象也将仅在deref_mut()
持续时间内存在.但是,由于deref_mut()
返回对Foo
对象的引用,因此我们将收到寿命不足"错误.
In general, if the reference to the temporary is used as an argument for a function call, the temporary lives only for that function call. This also includes the &self
(and &mut self
) parameter. So in get_foo().deref_mut()
, the Foo
object would also only live for the duration of deref_mut()
. But since deref_mut()
returns a reference to the Foo
object, we would get a "does not live long enough" error.
x.lock().unwrap().deref_mut()
当然也是如此-这就是我们得到错误的原因.
That's of course also the case for x.lock().unwrap().deref_mut()
-- that's why we get the error.
在deref运算符(*
)情况下,封闭块的临时生存期(等效于let
绑定).我只能假定这是编译器中的一种特殊情况:编译器知道对deref()
或deref_mut()
的调用总是返回对self
接收器的引用,因此借用临时变量没有任何意义.仅用于函数调用.
In the deref operator (*
) case, the temporary lives for the enclosing block (equivalent to a let
binding). I can only assume that this is a special case in the compiler: the compiler knows that a call to deref()
or deref_mut()
always returns a reference to the self
receiver, so it wouldn't make sense to borrow the temporary for only the function call.
这篇关于如果我从不将MutexGuard分配给变量,它在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!