在 T 和 UnsafeCell 之间转换是否安全且定义的行为?

2023-11-27

A 最近的问题正在寻找构建自我参照结构的能力。在讨论该问题的可能答案时,一个可能的答案涉及使用UnsafeCell用于内部可变性,然后通过“丢弃”可变性transmute.

这是这种想法的实际应用的一个小例子。我对这个例子本身并不很感兴趣,但它足够复杂,需要更大的锤子,比如transmute而不是仅仅使用UnsafeCell::new and/or UnsafeCell::into_inner:

use std::{
    cell::UnsafeCell, mem, rc::{Rc, Weak},
};

// This is our real type.
struct ReallyImmutable {
    value: i32,
    myself: Weak<ReallyImmutable>,
}

fn initialize() -> Rc<ReallyImmutable> {
    // This mirrors ReallyImmutable but we use `UnsafeCell` 
    // to perform some initial interior mutation.
    struct NotReallyImmutable {
        value: i32,
        myself: Weak<UnsafeCell<NotReallyImmutable>>,
    }

    let initial = NotReallyImmutable {
        value: 42,
        myself: Weak::new(),
    };

    // Without interior mutability, we couldn't update the `myself` field
    // after we've created the `Rc`.
    let second = Rc::new(UnsafeCell::new(initial));

    // Tie the recursive knot 
    let new_myself = Rc::downgrade(&second);

    unsafe {
        // Should be safe as there can be no other accesses to this field
        (&mut *second.get()).myself = new_myself;

        // No one outside of this function needs the interior mutability
        // TODO: Is this call safe?
        mem::transmute(second)
    }
}

fn main() {
    let v = initialize();
    println!("{} -> {:?}", v.value, v.myself.upgrade().map(|v| v.value))
}

这段代码似乎打印出了我所期望的内容,但这并不意味着它是安全的或使用定义的语义。

正在转变为UnsafeCell<T> to a T内存安全吗?它会调用未定义的行为吗?如果从相反的方向转变呢?T to an UnsafeCell<T>?


免责声明:此类事情的规则尚未一成不变。所以,目前还没有明确的答案。我将根据(a)LLVM 所做的/我们最终想要做的编译器转换类型,以及(b)我脑海中定义答案的模型类型进行一些猜测。

另外,我看到了两个部分:数据布局透视图和别名透视图。布局问题是NotReallyImmutable原则上,可以有一个完全不同的布局ReallyImmutable。我对数据布局不太了解,但是UnsafeCell变得repr(transparent)这是两种类型之间的唯一区别,我认为intent是为了这个工作。然而,你依赖于repr(transparent)“结构性”是指它应该允许您替换更大类型的事物,我不确定是否已在任何地方明确地写下这些内容。听起来像是后续 RFC 的提案,该提案扩展了repr(transparent)适当保证?

就别名而言,问题在于违反规则&T。我会这么说,只要你从未有过现场表演&T通过书写时可以在任何地方&UnsafeCell<T>,你很好——但我认为我们还不能保证这一点。让我们更详细地看看。

编译器视角

这里的相关优化是利用&T是只读的。因此,如果您重新排序最后两行(transmute和赋值),该代码可能是 UB,因为我们可能希望编译器能够“预取”共享引用后面的值并稍后重新使用该值(即在内联后)。

但在您的代码中,我们只会发出“只读”注释(noalias在 LLVM 中)之后transmute返回,数据确实从那里开始是只读的。所以,这应该是好的。

内存型号

我的记忆模型本质上是“最激进的”断言所有值始终有效,我认为即使该模型也应该适合您的代码。&UnsafeCell是该模型中的一个特例,有效性就停止了,并且没有提及该引用背后的内容。此刻transmute返回时,我们抓取它指向的内存并将其全部设为只读,即使我们通过“递归”执行此操作Rc(我的模型没有,但只是因为我找不到一个好的方法来让它这样做)你会没事的,因为你不会再变异了transmute。 (您可能已经注意到,这与编译器角度的限制相同。毕竟,这些模型的目的是允许编译器优化。;)

(作为旁注,我真的希望 miri 现在状况更好。似乎我必须尝试并在那里再次进行验证,因为那样我可以告诉你只在 miri 中运行你的代码,它会告诉你如果我的模型的那个版本适合你正在做的事情,那就是你:D)

我正在考虑目前仅检查“访问时”内容的其他模型,但尚未解决UnsafeCell该模型的故事还没有。这个例子表明,模型可能必须首先包含内存“相变”的方法UnsafeCell,但后来具有只读保证的正常共享。感谢您提出这个问题,这将成为一些值得思考的好例子!

所以,我想我可以说(至少从我这边)intent允许这种代码,这样做似乎不会阻止任何优化。我无法预测我们是否真的能够找到一个每个人都能同意并且仍然允许这样做的模型。

相反的方向:T -> UnsafeCell<T>

现在,这更有趣了。问题是,正如我上面所说,你不能有一个&T通过写作时生活UnsafeCell<T>。但这里的“生活”是什么意思呢?这是一个很难回答的问题!在我的一些模型中,这可能与“该类型的引用存在于某处并且生命周期仍然处于活动状态”一样弱,即,它可能与引用是否实际上无关used。 (这很有用,因为它可以让我们进行更多优化,例如将负载移出循环,即使我们无法证明循环曾经运行过 - 这将引入对其他未使用的引用的使用。)&T is Copy,你甚至无法真正摆脱这样的引用。所以,如果你有x: &T, 然后let y: &UnsafeCell<T> = transmute(x), 老人x仍然存在并且它的生命周期仍然活跃,所以写通过y很可能是UB。

我认为你必须以某种方式限制别名&T允许,非常小心地确保没有人仍然持有这样的参考。我不会说“这是不可能的”,因为人们总是让我感到惊讶(尤其是在这个社区中;)但说实话,我想不出一种方法来实现这项工作。我很好奇你是否有一个你认为这是合理的例子。

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

在 T 和 UnsafeCell 之间转换是否安全且定义的行为? 的相关文章

随机推荐

  • Xcode6 无法将服务器的响应解析为 plist

    我正在尝试将我的苹果开发者帐户添加到 Xcode Beta 3 以便我可以在 iPhone 中运行应用程序 但没有成功 我转到 Xcode gt 首选项 gt 帐户 gt 添加 Apple ID 然后我输入我的用户凭据并按 确定 我不认为这
  • 将视频插入图库 [Android Q]

    记录一个SurfeceView我正在使用第 3 方library 这个库需要一个路径 在我的例子中保存的输出 录制的视频 是保存的视频路径 mRenderPipeline EZFilter input this effectBmp addF
  • Codeblocks 的 Boost 库设置 [关闭]

    很难说出这里问的是什么 这个问题模棱两可 含糊不清 不完整 过于宽泛或言辞激烈 无法以目前的形式合理回答 如需帮助澄清此问题以便重新打开 访问帮助中心 如何在 Codeblocks Windows 中使用 boost 库 从 boost 网
  • 内置 WPF 控件如何管理附加事件的事件处理程序?

    我知道当您将对象注册到鼠标的附加事件时 就会出现内存泄漏 这就是为什么你需要使用弱事件模式 我对这种模式有一个问题 如果您想使用它 则无法在 XAML 代码中定义处理程序 对我来说 这意味着像这样的每个代码都会泄漏
  • 我的流媒体音频播放器在通话期间继续播放 - 无法让它停止! (安卓)

    我有一个由多项活动和一项服务组成的应用程序 主要活动是用于流音频的 UI 当用户按下播放按钮时 服务就会启动 传输音频并读取元数据 然后 元数据会被推送到 UI 如果可见 和通知栏 在接到电话之前 一切都按预期运行 我曾假设通过使用标准媒体
  • 当主机代码中声明常量内存时,CUDA如何访问设备内核中的常量内存?

    根据记录 这是家庭作业 因此请记住这一点 或多或少地提供帮助 我们使用常量内存来存储 掩码矩阵 该矩阵将用于在更大的矩阵上执行卷积 当我在主机代码中时 我使用 cudaMemcpyToSymbol 将掩码复制到常量内存 我的问题是 一旦将其
  • 访问 DI 容器

    我正在开始一个新项目并建立工作基础 出现了一些问题 我可能会在这里问很多问题 希望我能找到一些答案 第一步是处理对象的依赖关系 我决定采用依赖注入设计模式 我对此有些陌生 来处理应用程序的所有这些问题 在实际编码时我遇到了一个问题 如果一个
  • Rails:发出 POST 请求时无法验证 CSRF 令牌的真实性

    我要实现POST request给我本地的开发人员 如下所示 HTTParty post http localhost 3000 fetch heroku body gt type gt product 但是 从服务器控制台报告 Start
  • 以编程方式更改系统显示大小 Android N

    背景 Android N 具有更改系统的功能Display Size从设置中 除了以前存在的更改功能之外Font Size 更改显示尺寸 图片来源 pcmag com Question 如果一个应用程序有android permission
  • 使用 Java 为 OSX 编写可执行 .sh 文件

    所以我试图编写一个可执行的 sh 文件 这就是我目前编写的方式 Writer output null try output new BufferedWriter new FileWriter file2 output write shell
  • 从 WSL 2 内部访问在 Windows 中运行的本地主机 [关闭]

    Closed 这个问题不符合堆栈溢出指南 目前不接受答案 我正在运行本地AEM服务器 in my Windows机 服务器正在运行本地主机 4502 我正在使用运行的 Ubuntu 发行版WSL 2为了我的发展 我想访问本地主机 4502在
  • 如何合并 Drupal 数据库更改

    我们目前使用 SVN 存储库来确保每个人的本地环境保持最新 然而 Drupal 网站开发有些棘手 因为您编写的任何自定义代码 例如 为节点主体编写的 PHP 代码 都存储在数据库中 并且 SVN 工作副本无法识别更改 目前有几个开发人员正在
  • 指向不完整类型的指针可以不完整吗?

    Can int 是一个不完整的类型 C 2018 6 2 5 1 说 在翻译单元内的各个点处 对象类型可以是不完整的 缺乏足够的信息来确定该类型对象的大小 或complete 有足够的信息 因此 如果类型的大小已知 则该类型似乎是完整的 6
  • Chrome DevTools 和扩展程序中的内容脚本之间进行通信

    我已经读过this但它不起作用 我做了很多搜索和实验都无济于事 我正在编写一个 Chrome 扩展 大控制台 目标是为 Chrome 开发者工具构建更好的控制台选项卡 这意味着我想在页面上下文中执行用户输入代码 并访问页面上的 DOM 和其
  • 打开 O_CREAT | Linux 中 NFS 上的 O_EXCL?

    当在 Linux 2 6 内核和 NFSv3 中时open fname O CREAT O EXCL 生效了吗 目前的规范open 2 系统调用文档 http www kernel org doc man pages online page
  • Notepad ++ - 只保留数字

    我有一个包含数字和字符的文本文件 阿拉伯语 像这样 943894 964737 编号 1045051 10653 10653 to this 943894964737 1045051 10653 我想删除除数字之外的所有内容 我在这里查看了
  • 下拉列表中选项的粗体部分

    有没有办法仅将下拉列表选项中的部分文本加粗或者这是不可能的 我知道您可以使用 CSS 设置整个选项的样式 但这不是我想要的 并且选项标签内的 HTML 不会呈现 这是下拉列表项的示例 一些ID 身份证件说明 你不可以做这个 您会发现 那里
  • 从plist中检索数据

    我有一个 plist 里面有一个数组 然后是一组字典元素 如何将数据从 plist 检索到我的数组 如何在一个数组中获取类别名称 Objective C Read plist from bundle and get Root Diction
  • 在客户端定义传输类型

    我需要对 IE 使用 jsonp polling 对 Firefox 使用 xhr polling 所以我 尝试在客户端定义传输类型 如下所示 if Firefox s d d test navigator userAgent test f
  • 在 T 和 UnsafeCell 之间转换是否安全且定义的行为?

    A 最近的问题正在寻找构建自我参照结构的能力 在讨论该问题的可能答案时 一个可能的答案涉及使用UnsafeCell用于内部可变性 然后通过 丢弃 可变性transmute 这是这种想法的实际应用的一个小例子 我对这个例子本身并不很感兴趣 但