如果我从不将 MutexGuard 分配给变量,那么它在哪里?

2023-12-26

我不明白“哪里”MutexGuard在内部代码块中是。互斥锁被锁定并展开,产生MutexGuard。不知何故,这段代码设法取消引用MutexGuard然后可变地借用该对象。哪里去了MutexGuard去?另外,令人困惑的是,这种取消引用不能替换为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: 因为*x.lock().unwrap()执行一个隐性借用 https://doc.rust-lang.org/reference/expressions.html#implicit-borrows操作数的x.lock().unwrap(),操作数被视为位置上下文。但由于我们的实际操作数不是位置表达式,而是值表达式,它被分配到一个未命名的内存位置(基本上是一个隐藏的let捆绑)!

请参阅下文以获得更详细的解释。


位置表达式和值表达式

在我们深入讨论之前,首先介绍两个重要术语。 Rust 中的表达式分为两大类:位置表达式和值表达式。

  • 地点表达表示一个有家(内存位置)的值。例如,如果您有let x = 3; then x是地点表达。历史上这被称为左值表达式.
  • 值表达式表示一个没有家的值(我们只能使用该值,没有与之关联的内存位置)。例如,如果您有fn bar() -> i32 then bar()是一个值表达式。文字如3.14 or "hi"也是价值表达。历史上这些被称为右值表达式.

有一个很好的经验法则可以检查某物是否是位置或值表达式:“将其写在作业的左侧有意义吗?”。如果是的话(比如my_variable = ...;)它是一个地点表达式,如果不是(比如3 = ...;) 这是一个值表达式。

也存在地点情境 and 价值背景。这些基本上是可以放置表达式的“槽”。只有几个地点情境,这(通常,见下文)需要地点表达:

  • (复合)赋值表达式的左侧 (⟨place context⟩ = ...;, ⟨place context⟩ += ...;)
  • 借位表达式的操作数 (&⟨place context⟩ and &mut ⟨place context⟩)
  • ...还有更多

请注意,地点表达严格来说更“强大”。它们可以毫无问题地用在值上下文中,因为它们also代表一个值。

(参考文献中的相关章节 https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions)

暂时的生命周期

让我们构建一个小虚拟示例来演示 Rust 所做的事情:

struct Foo(i32);

fn get_foo() -> Foo {
    Foo(0)
}

let x: &Foo = &get_foo();

这有效!

我们知道表达式get_foo() is a 价值表达。我们知道借位表达式的操作数是地点背景。那么为什么会编译呢?没有地点情境 need 地点表达?

Rust 创造临时的let绑定!从参考资料 https://doc.rust-lang.org/reference/expressions.html#temporary-lifetimes:

当在大多数地方表达式上下文中使用值表达式时,会创建一个临时的未命名内存位置并初始化为该值,并且表达式的计算结果会改为该位置[...]。

所以上面的代码相当于:

let _compiler_generated = get_foo();
let x: &Foo = &_compiler_generated;

这就是让你Mutex示例工作:MutexLock被分配到一个临时的未命名内存位置!那就是它居住的地方。让我们来看看:

&mut *x.lock().unwrap();

The x.lock().unwrap()部分是一个值表达式:它具有类型MutexLock并由函数返回(unwrap()) 就像get_foo()多于。那么只剩最后一个问题:deref 的操作数是*操作地点上下文?我在上面的名次竞赛列表中没有提到它......

隐性借用

难题的最后一部分是隐式借用。从参考资料 https://doc.rust-lang.org/reference/expressions.html#implicit-borrows:

某些表达式将通过隐式借用表达式将其视为地点表达式。

其中包括“解引用运算符的操作数(*)"!任何隐式借用的所有操作数都是位置上下文!

所以因为*x.lock().unwrap()执行隐式借位,操作数x.lock().unwrap()是一个位置上下文,但由于我们的实际操作数不是一个位置,而是一个值表达式,因此它被分配到一个未命名的内存位置!

为什么这不起作用deref_mut()

有一个重要的细节是“临时生命周期”。我们再看一下引用:

当在大多数地方表达式上下文中使用值表达式时,会创建一个临时的未命名内存位置并初始化为该值,并且表达式的计算结果会改为该位置[...]。

根据情况,Rust 选择不同生命周期的内存位置!在里面&get_foo()上面的例子中,临时的未命名内存位置具有封闭块的生命周期。这相当于隐藏let我上面展示的绑定。

However,这个“临时未命名内存位置”并不总是等同于let捆绑!我们来看看这个案例:

fn takes_foo_ref(_: &Foo) {}

takes_foo_ref(&get_foo());

在这里,Foo价值只在持续时间内存在takes_foo_ref打电话,不要再长了!

一般来说,如果对临时变量的引用用作函数调用的参数,则临时变量仅在该函数调用中存在。这还包括&self (and &mut self) 范围。所以在get_foo().deref_mut(), the Foo对象也只会存活一段时间deref_mut()。但是由于deref_mut()返回对的引用Foo对象,我们会得到一个“活得不够长”的错误。

这当然也是如此x.lock().unwrap().deref_mut()-- 这就是我们收到错误的原因。

在 deref 运算符中 (*)情况下,封闭块的临时生命周期(相当于let捆绑)。我只能假设这是编译器中的一个特殊情况:编译器知道调用deref() or deref_mut()始终返回对的引用self接收者,因此仅借用临时函数调用是没有意义的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果我从不将 MutexGuard 分配给变量,那么它在哪里? 的相关文章

随机推荐

  • [13]:Array 的未定义方法“assign_attributes”

    我的应用程序设置为 当 Product sold 属性为 1 时 表示商品已售出 并且不会显示在商店视图中 我正在尝试获取它 以便当客户签出时 购买商品时会更新product sold 属性 以下是我的控制器中应将 Product sold
  • 如何使用CSS在图像上添加覆盖颜色

    如果我有这样的图像 img src inshot1 jpg width 100px height 100px 悬停时我希望该块被某种颜色覆盖 例如 当您将鼠标悬停在其上时 您会看到一块具有相同高度和宽度的红色块 那么基本上是叠加吗 您可以通
  • 单个应用程序二进制文件如何支持 64 位和 32 位应用程序

    我们可以看到苹果的公告here https developer apple com news 根据这个文档 我们可以提交相同的二进制文件 支持 32 位和 64 位 我找到了一个堆栈溢出答案here https stackoverflow
  • 输入字段问题 - 关闭窗口但保持 Python 运行 [重复]

    这个问题在这里已经有答案了 有点长的问题 我正在创建一个输入字段 在 skrx 的主要帮助下 该字段显示在定制屏幕上 我已经对其进行了编程 以便当我按 Enter 键时 屏幕应该自行关闭 目前这个 pygame display quit 感
  • 如何在 TensorFlow 中有效地分配给张量的切片

    我想为 TensorFlow 2 x 中的一个模型中的输入张量切片分配一些值 我正在使用 2 2 但准备接受 2 1 的解决方案 我想做的一个非工作模板是 import tensorflow as tf from tensorflow ke
  • 如何调试通过 Windows Scheduler 运行的 exe? [复制]

    这个问题在这里已经有答案了 我正在使用 Windows Scheduler 来运行我编写的 exe 当调度程序启动我的 exe 时 如何跳入调试会话 更新1 我曾想过执行 Thread Sleep 然后附加到进程 当我尝试时 它说调试器已经
  • 加载 php 模块时出错

    我在 etc php 7 0 cli php ini 中添加了curl模块 extension usr lib php5 20121212 curl so 但是当我重新启动 php 时 我会 php m 我收到此错误 curl Unable
  • 如何在maven2中进行ear build?

    我对maven2很陌生 直到昨天我才成功地从maven2构建了war 我的下一个目标是为 war 文件构建 Ear 文件 其中还包括一些 jar 文件 你能帮忙吗 我将非常感激 问候 咬伤85 的官方文档Maven EAR 插件 http
  • msdeploy 的 Manifest.xml 文件中的 runCommand 提供程序

    我试图通过使用清单文件中的 runCommand 提供程序 通过 msdeploy 将运行批处理脚本 更多 前提是我能弄清楚 作为部署过程的一部分 这就是我的清单文件的样子
  • 如何加速 R 中的“expand.grid()”?

    我正在尝试加快创建包含两个向量之间所有可能组合的表 我们可以从以下位置获取此功能base R当我们使用expand grid 然而 我想知道我们是否可以使用以下工具实现相同的结果 但速度更快 collapse 包裹 StackOverflo
  • 在 PHPUnit (CIUnit) 中使用 YAML 文件作为数据提供者

    我正在使用 PHP CodeIgniter 框架编写一个应用程序 我正在尝试使用 CI Unit 扩展名为 PHPUnit 来测试应用程序 为了测试模型 我尝试加载 PHPUnit 文档中定义的 YAML 数据提供程序 但收到错误 如果我伪
  • Android Studio 不显示按钮的属性

    我在 Android Studio 中打开了一个空白的 Android 应用程序 当我尝试编辑按钮时 属性不存在 我是 Android 开发新手 正在遵循显示如何更改按钮中的文本的指南 但属性菜单缺少指南中看到的选项 Using 安卓工作室
  • API网关集成中使用代理集成有什么用?

    我正在尝试在 API Gateway API 和 VPC 背后的资源之间进行集成 创建集成时 有一个选项可供选择 使用代理集成 即使我没有选中此复选框并部署 API 我也可以通过 NLB 使 API 网关连接到 VPC 中的目标资源 所以
  • 在 php 中执行条件时出现问题[已关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 press GET sell echo press OUTPUT IS SELL if press SELL header Loc
  • Android 共享首选项在删除文件后仍然可见

    我正在尝试使用共享首选项的应用程序 当我从中删除首选项文件时数据 data com your package name shared prefs mySharedPref xml手动使用 Android 监视器 应用程序仍然能够读取首选项值
  • DataGridView 级联/相关组合框列

    因此 我时常在 Winforms 的遗留应用程序中工作 并不总是熟悉绑定对象的最佳实践 基本上我有一个三部分集 其中有两个人 他们可能只有一种产品 但该产品可能会导致拥有不同的 SKU 集 有没有办法从第一个组合框的值触发组合框的事件和填充
  • Java EE 中的拦截器是什么?

    我正在尝试澄清关于 Java EE 中的拦截器的概念 我已经阅读了 Java EE 规范 但我对此有点困惑 请为我提供一些有用的链接或教程 以澄清我的概念 我们如何 何时 为何使用拦截器 拦截器用于实现业务逻辑中的横切关注点 例如日志记录
  • 从 activemq 主题获取消息时,logstash 输入速度非常慢

    我已经在logstash中配置了JMS输入来订阅JMS主题消息并将消息推送到弹性搜索 input jms id gt my first jms yaml file gt D softwares logstash 6 4 0 config j
  • 访问派生类中的受保护成员

    我昨天遇到了一个错误 虽然很容易解决 但我想确保我正确理解了 C 我有一个带有受保护成员的基类 class Base protected int b public void DoSomething const Base that b tha
  • 如果我从不将 MutexGuard 分配给变量,那么它在哪里?

    我不明白 哪里 MutexGuard在内部代码块中是 互斥锁被锁定并展开 产生MutexGuard 不知何故 这段代码设法取消引用MutexGuard然后可变地借用该对象 哪里去了MutexGuard去 另外 令人困惑的是 这种取消引用不能