Rust 中涉及临时对象的销毁顺序

2024-01-27

在 C++ 中(如果错误,请纠正我),通过常量引用进行的临时绑定应该比它所绑定的表达式寿命更长。我认为 Rust 也是如此,但在两种不同的情况下我得到了两种不同的行为。

考虑:

struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }

struct B(*const A);
impl Drop for B { fn drop(&mut self) { println!("Drop B.") } }

fn main() {
    let _ = B(&A as *const A); // B is destroyed after this expression itself.
}

输出是:

Drop B.
Drop A.

这就是您所期望的。但现在如果你这样做:

fn main() {
    let _b = B(&A as *const A); // _b will be dropped when scope exits main()
}

输出是:

Drop A.
Drop B.

这不是我所期望的。

这是有意的吗?如果是的话,那么这两种情况下行为差异的理由是什么?

我正在使用 Rust 1.12.1。


临时变量在语句末尾被删除,就像在 C++ 中一样。然而,IIRC,Rust 中的销毁顺序未指定(我们将在下面看到其后果),尽管当前的实现似乎只是以与构造相反的顺序删除值。

之间有很大的区别let _ = x; and let _b = x;. _不是 Rust 中的标识符:它是通配符模式。由于此模式未找到任何变量,因此最终值实际上在语句末尾被删除。

另一方面,_b是一个标识符,因此该值绑定到具有该名称的变量,这将延长其生命周期直到函数结束。但是,那A实例仍然是临时的,因此它将在语句末尾被删除(我相信 C++ 也会这样做)。由于语句结束早于函数结束,因此A首先删除实例,然后B第二个实例被丢弃。

为了使这一点更清楚,让我们添加另一条语句main:

fn main() {
    let _ = B(&A as *const A);
    println!("End of main.");
}

这会产生以下输出:

Drop B.
Drop A.
End of main.

到目前为止,一切都很好。现在让我们尝试一下let _b;输出是:

Drop A.
End of main.
Drop B.

正如我们所看到的,Drop B之后打印End of main.。这表明B实例在函数结束之前一直处于活动状态,这解释了不同的销毁顺序。


现在,让我们看看如果我们修改会发生什么B采用具有生命周期的借用指针而不是原始指针。实际上,让我们更进一步,删除Drop暂时实现一下:

struct A;
struct B<'a>(&'a A);

fn main() {
    let _ = B(&A);
}

这编译得很好。在幕后,Rust 为两个对象分配了相同的生命周期A实例和B实例(即,如果我们引用B例如,它的类型是&'a B<'a>两者都在哪里'a是完全相同的寿命)。当两个值具有相同的生命周期时,我们必然需要在另一个值之前删除其中一个,并且如上所述,顺序是未指定的。如果我们加回会发生什么Drop实施?

struct A;
impl Drop for A { fn drop(&mut self) { println!("Drop A.") } }

struct B<'a>(&'a A);
impl<'a> Drop for B<'a> { fn drop(&mut self) { println!("Drop B.") } }

fn main() {
    let _ = B(&A);
}

现在我们收到编译器错误:

error: borrowed value does not live long enough
 --> <anon>:8:16
  |
8 |     let _ = B(&A);
  |                ^ does not live long enough
  |
note: reference must be valid for the destruction scope surrounding statement at 8:4...
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^
note: ...but borrowed value is only valid for the statement at 8:4
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^
help: consider using a `let` binding to increase its lifetime
 --> <anon>:8:5
  |
8 |     let _ = B(&A);
  |     ^^^^^^^^^^^^^^

由于两者A实例和B实例被分配了相同的生命周期,Rust 无法推断这些对象的销毁顺序。该错误来自于 Rust 拒绝实例化B<'a>与对象本身的生命周期B<'a>实施Drop(这条规则是由于以下原因而添加的RFC 769 https://github.com/rust-lang/rfcs/pull/769Rust 1.0 之前)。如果允许的话,drop将能够访问已经被删除的值!然而,如果B<'a>不执行Drop,那么它是允许的,因为我们知道没有代码会尝试访问B结构体被删除时的字段。

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

Rust 中涉及临时对象的销毁顺序 的相关文章

随机推荐

  • 有什么方法可以获取 C# 中调用对象的引用吗?

    我想知道是否可以 例如 遍历堆栈帧 检查每个调用对象以查看是否与接口匹配 如果是 则从中提取一些数据 是的 我知道这是不好的做法 我想知道这是否可能 不 不存在 至少不使用某种描述的分析 调试 API 就不会 您可以遍历堆栈来查找调用met
  • $onloaded 为新引用触发一次 AngularFire 0.5.0

    假设我们正在使用推送状态导航路线 locationProvider html5Mode true 有两条路线有两个不同的控制器 stateProvider state one url one templateUrl one html con
  • 以编程方式更改 R.string 的值?

    我正在寻找一种动态更改字符串资源值的方法 我尝试使用反射 但它声称 字段值无效 我在布局中使用字符串作为值 但需要将它们交换为不同的语言 请参阅下面所附的代码 public class Lang public static void lan
  • 执行一些代码,然后进入交互节点

    有没有办法在进入 Node js 中的交互模式之前执行一些代码 在文件中或从字符串中 并不重要 例如 如果我创建一个脚本 preamble js其中包含 console log preamble executed poor guy 和用户类
  • 使用 gradle 对嵌入 glassfish 的 jar 进行容器测试失败

    我正在使用 JUnit 和嵌入式 glassfish 来使用 CDI 测试一些代码 这看起来相当简单 但我一定错过了一些东西 我创建了一个简单的项目来演示该问题 https github com dantwinkler container
  • 如何在用户离开页面时删除文件?

    我有一个表单 其操作设置为 php 文件 verify php 在表单中 我制作了一个非常漂亮的图片上传部分 提交表单后 将打开 verify php 以验证用户填写的表单 表单值全部放置在 verify php 页面上的另一个表单内 仅将
  • Xamarin.Forms 和 Xamarin 跨平台之间的区别

    我已经使用 Xamarin 环境 2 年了 主要使用 Xamarin Forms 但是 我不知道 Xamarin 跨平台是什么 谁能提供一些结构差异 当我们谈论 Xamarin 时 本机应用程序的开发有两种方法 传统的 Xamarin 方法
  • ASP.NET Identity:授权后更新外部声明

    我将 ASP NET Identity 与多个外部登录提供程序一起使用 并且需要处理以下场景 1 用户使用外部服务登录 假设是 Facebook 应用程序从 Facebook 获取一些信息 名字和姓氏 电子邮件 出生日期等 包含此信息的声明
  • 具有相同列表的两个变量具有不同的 ID...为什么呢?

    尝试理解以下内容 为什么Python为相同的列表分配的ID不同 x 1 2 3 y 1 2 3 id x id y True id x 11428848 id y 12943768 每一个独特的object在Python中有自己的ID 它与
  • Python 中的 C# Parallel.Foreach 等效项

    我有 96 个 txt 文件需要处理 现在我正在使用 for 循环并一次执行一个 这个过程非常慢 生成的 96 个文件不需要合并 有没有办法让它们并行运行 比如 C 中的 Parallel foreach 当前代码 for src name
  • 如何在行内块元素之间添加空间?

    明确地说 我不想remove内联块元素之间的空间 我想要add it 我想要的是有一个菜单项网格 一行可以有 2 3 或 4 个项目 我希望使用媒体查询来实现 我怎样才能在我的 li 项目之间添加空间 但也有每行左右两侧无边距 填充无法解决
  • 如何显示mysql数据库的图像和描述

    我正在尝试显示图像 然后在图像下显示描述 我得到了图像显示 但我对描述感到困惑 我无法让它发挥作用 在我的代码下面显示图像和描述 Get images from the database query db gt query SELECT F
  • Mysqldump'不被识别为内部或外部命令可操作程序或批处理文件

    我一直在尝试使用任务计划程序创建批处理来备份 MySQL 数据库 FOR F tokens 1 4 DELIMS F IN date T DO set v date F G H FOR F tokens 1 4 DELIMS F IN ti
  • 错误:(35, 24) 错误:在最新的 Glide 中找不到符号方法 crossFade()

    我有一个使用 Glide 3 5 2 的旧项目 下面的工作正常 Glide with context load url override IMAGE SIZE FIX IMAGE SIZE FIX crossFade placeholder
  • Laravel:从视图调用base_controller中定义的函数

    在使用laravel框架时 如何在视图中调用base controller中定义的函数 例如 class Base Controller extends Controller public static function format so
  • 在 EC2 实例内运行的 docker 容器内运行的 Web 服务器响应非常慢

    我有一个 Web 服务器在 AWS EC2 Ubuntu 实例的 docker 容器内运行 当我向 Web 服务器发送请求时 得到响应的速度非常慢 大多数情况下 20 秒以上 尽管响应时间各不相同 但它不会超时 Web 服务器是一个非常轻量
  • iframe 中的 419 页面已过期 Laravel 页面

    我正在打开一个使用开发的页面Laravel 7在 Angular 项目的 iframe 内 页面正在显示记录 但是在创建或更新记录时 页面会抛出 419 错误 我已经更新了 same site gt none 但没有帮助 这两个项目都部署在
  • 边框半径隐藏的溢出在 Chrome 中不起作用

    不确定这是否是 chrome 特定的错误或什么 但是当我在具有边框半径隐藏溢出的父元素上转换子元素时 溢出是可见的 而转换就位 var wrapper document getElementsByClassName wrapper 0 im
  • 在 Sublime Text 3 中运行 Python 调试器 (pdb)

    如何设置 python 调试器 pdb https docs python org 2 library pdb html Sublime Text 3 中的断点 Both 崇高REPL https packagecontrol io pac
  • Rust 中涉及临时对象的销毁顺序

    在 C 中 如果错误 请纠正我 通过常量引用进行的临时绑定应该比它所绑定的表达式寿命更长 我认为 Rust 也是如此 但在两种不同的情况下我得到了两种不同的行为 考虑 struct A impl Drop for A fn drop mut