如果我从不将MutexGuard分配给变量,它在哪里? [英] Where is a MutexGuard if I never assign it to a variable?

查看:85
本文介绍了如果我从不将MutexGuard分配给变量,它在哪里?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不了解内部代码块中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);
}

推荐答案

摘要:因为*x.lock().unwrap()执行

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; then x 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 then bar() is a value expression. Literals like 3.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屋!

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