这个错误是由于编译器对 RefCell 的特殊了解造成的吗?

2024-01-08

fn works<'a>(foo: &Option<&'a mut String>, s: &'a mut String) {}
fn error<'a>(foo: &RefCell<Option<&'a mut String>>, s: &'a mut String) {}

let mut s = "hi".to_string();

let foo = None;
works(&foo, &mut s);

// with this, it errors
// let bar = RefCell::new(None);
// error(&bar, &mut s);

s.len();

如果我添加两行注释,则会出现以下错误:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> <anon>:16:5
   |
14 |     error(&bar, &mut s);
   |                      - mutable borrow occurs here
15 |     
16 |     s.len();
   |     ^ immutable borrow occurs here
17 | }
   | - mutable borrow ends here

的签名works() and errors()看起来相当相似。但显然编译器知道你可以用RefCell,因为借用检查器的行为不同。

我什至无法“隐藏”RefCell在我自己的另一种类型中,但编译器仍然总是做正确的事情(如果发生错误RefCell可用于)。编译器如何知道所有这些东西以及它是如何工作的?编译器是否将类型标记为“内部可变性容器”或类似的东西?


RefCell<T>包含一个UnsafeCell<T> https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html这是一个特殊的语言项 https://doc.rust-lang.org/unstable-book/language-features/lang-items.html. It is UnsafeCell这会导致错误。您可以检查:

fn error<'a>(foo: &UnsafeCell<Option<&'a mut String>>, s: &'a mut String) {}

...

let bar = UnsafeCell::new(None);
error(&bar, &mut s);

但该错误并不是由于编译器识别出 UnsafeCell 引入了内部可变性,而是由于UnsafeCell is 不变的 https://doc.rust-lang.org/nomicon/subtyping.html事实上,我们可以使用以下命令重现该错误幻象数据 https://doc.rust-lang.org/std/marker/struct.PhantomData.html:

struct Contravariant<T>(PhantomData<fn(T)>);

fn error<'a>(foo: Contravariant<&'a i32>, s: &'a mut String) {}

...

let bar = Contravariant(PhantomData);
error(bar, &mut s);

甚至是一生中逆变或不变的任何事物'a:

fn error<'a>(foo: Option<fn(&'a i32)>, s: &'a mut String) {}

let bar = None;
error(bar, &mut s);

无法隐藏 RefCell 的原因是方差是通过结构的字段导出的。一旦你使用过RefCell<T>某个地方,无论多深,编译器都会弄清楚T是不变的。


现在让我们看看编译器如何判断E0502错误。首先,重要的是要记住编译器必须在这里选择两个特定的生命周期:表达式类型的生命周期&mut s ('a)和类型的生命周期bar(我们称之为'x)。两者都受到限制:前一世'a必须短于范围s,否则我们最终会得到一个比原始字符串寿命更长的引用。'x必须大于范围bar,否则我们可以通过访问悬空指针bar(如果类型具有生命周期参数,则编译器假定该类型可以访问具有该生命周期的值)。

有了这两个基本限制,编译器将执行以下步骤:

  1. 方式bar is Contravariant<&'x i32>.
  2. The error函数接受任何子类型Contravariant<&'a i32>, where 'a是那个的生命周期&mut s表达。
  3. Thus bar应该是一个子类型Contravariant<&'a i32>
  4. Contravariant<T>是逆变的T,即如果U <: T, then Contravariant<T> <: Contravariant<U>.
  5. 因此子类型关系可以满足&'x i32 is a 超型 of &'a i32.
  6. Thus 'x应该shorter than 'a, i.e. 'a should outlive 'x.

类似地,对于不变类型,派生关系是'a == 'x,对于协变,'x比寿命长'a.

现在,这里的问题是type of bar生存到范围结束(根据上述限制):

    let bar = Contravariant(PhantomData);   // <--- 'x starts here -----+
    error(bar,                              //                          |
          &mut s);                          // <- 'a starts here ---+   |
    s.len();                                //                      |   |
                                            // <--- 'x ends here¹ --+---+
                                            //                      |
                                            // <--- 'a ends here² --+
}

// ¹ when `bar` goes out of scope
// ² 'a has to outlive 'x

在逆变和不变的情况下,'a寿命长(或等于)'x意味着声明s.len()必须包含在范围内,导致借用错误。

只有在协变的情况下,我们才能使范围'a比。。。短'x,允许临时对象&mut s之前被删除s.len()被称为(意思是:在s.len(), s不再被视为借用):

    let bar = Covariant(PhantomData);       // <--- 'x starts here -----+
                                            //                          |
    error(bar,                              //                          |
          &mut s);                          // <- 'a starts here --+    |
                                            //                     |    |
                                            // <- 'a ends here ----+    |
    s.len();                                //                          |
}                                           // <--- 'x ends here -------+
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这个错误是由于编译器对 RefCell 的特殊了解造成的吗? 的相关文章

随机推荐

  • 无法加载 tsc.ps1,因为该系统上禁用了运行脚本

    在 PowerShell 上 执行时收到错误消息tsc 这以前从未发生过 我不确定是否应该混合 PowerShell 安全设置来纠正此问题 例如基于此 PowerShell 表示 此系统上禁用了脚本执行 https stackoverflo
  • 如何检查数据库中是否存在 Oracle 视图?执行查询之前

    我需要知道一种在执行查询之前从 Java 桌面应用程序检查当前数据库中是否存在 Oracle 视图的方法 否则我会遇到很多麻烦 提前致谢 您随时可以查询Oracle数据字典 就像是 SELECT COUNT FROM all views W
  • Haskell 应用变压器的示例

    www haskell org 上的 wiki 告诉我们以下有关 Applicative Transformer 的信息 那么应用变压器在哪里呢 答案是 我们不需要适用函子的特殊变压器 因为它们可以以通用方式组合 http www hask
  • 为什么 MyPage._meta.get_field("title").verbose_name 会更改 Wagtail 中所有标题的标签?

    我的 Wagtail 项目中有几个应用程序 其中之一是 新闻 其中包含新闻 页面 我想在管理中将标题的标签 标题 覆盖为 标题 News meta get field title verbose name Headline 结果 我在所有应
  • 如何使用 Ant 运行类别/套件中的所有 JUnit 测试?

    我在类似于中描述的设置中使用 JUnit 类别和 ClassPathSuite这个答案 https stackoverflow com questions 2176570 how to run all tests belonging to
  • Android - 短信广播接收器

    我一直在努力得到this https stackoverflow com questions 1944102 android sms receiver not working程序可以工作 但到目前为止还没有运气 我找不到我哪里做错了 不知道
  • Clojure 更新映射多个值的惯用方法

    这可能很简单 但我就是无法克服它 我有一个嵌套映射的数据结构 如下所示 def m 1 1 2 2 5 3 10 2 1 2 2 50 3 25 3 1 42 2 23 3 4 我需要设置每个m i i 0 这在非函数式语言中很简单 但我无
  • 将 RSA 私钥导出到 RSAPublicKey 的命令行工具

    今天我发现有两种带有 PEM 格式标头的公钥格式 例如 X 509 SubjectPublicKeyInfo PEM header BEGIN PUBLIC KEY 对应于短标头形式 BEGIN PUBLIC KEY MIIBIjANBgk
  • 我可以在绕过加载命名空间的同时加载 RData 文件吗?

    假设我的一些用户无法更改他们的 R 环境 但我需要他们能够打开 RData 文件 这些环境文件需要加载一个包 确切地说是httpuv 我们不关心包 我们不需要它的功能 我们只需要获取数据 有没有办法强制 R 在加载 RData 文件时绕过加
  • Nim 中的价值与参考模型是什么?

    NOTE 我不是在问指针和引用之间的区别 对于这个问题来说它是完全无关的 我找不到明确说明的一件事 Nim 使用什么模型 就像 C 一样 你有价值观并且与new您创建指向数据的指针 在这种情况下 变量可以保存指向指向 数据的指针的指针 或者
  • Android 中的 BLE 广告

    我正在开发一个应用程序来在 android 中发送 BLE 广告包 我使用 AdvertiseData 和 AdverstiseSettings 类来生成广告数据包 但是当我执行 StartAdvertising 时 它总是给我一个错误代码
  • 从 ant 通过 sshexec 将密码传递给“su”命令

    有什么方法可以将密码传递给linux su 命令吗 我正在尝试使用 sshexec 和 Ant 自动化部署 作为其中的一部分 我需要执行 su 命令 但我找不到为其提供密码的方法 su 命令没有像 sudo 那样的 S 开关 我尝试在 ss
  • 调整表单大小,同时保持纵横比

    我有一个窗口 在其中显示图片 我希望用户能够调整此窗口的大小 但是 保持其与图像的宽高比相同 这样窗口上就不会出现大的空白区域 我在 OnResize 事件中尝试的是这样的 DragWidth Width DragHeight Height
  • 在 Google 应用程序脚本、电子表格中添加前导零

    我有一个主表 我正在从那里复制值并将其放入新选项卡中 但是当我在新选项卡中显示数字时 它会去掉前导零 例如 在我的主表中 我有 00734 我将其复制到新选项卡 它显示为 734 这是我用来复制值的代码 var values active
  • 如何为 Objective-C 和 Swift 制作一个通用的 iOS 库?

    我需要为 iOS 创建一个库 框架或静态库 我还没有决定 它可以在 Objective C 和 Swift 项目中使用 这样做的最佳方法是什么 在我看来 我有三个选择 用 Objective C 编写库并添加对 Swift 的支持 桥接头文
  • 如何使用 Fakes Framework shims 模拟/隔离 VS 2012 中的内部类?

    所以 问题是我的程序集中有一堆内部类 由我想要测试的类使用 由于访问器已从 VS2012 中删除 我可以使用 InternalsVisibleTo 这非常有效 除了当我尝试 shimify 我的内部类时 它们对 Fakes 框架不可见 我还
  • 如何使用 CSS 调整图像大小以适合其容器

    我想制作具有恒定宽度和高度的 div 其中包含尺寸未知的子图像 像这样的东西 我最接近的是max height inherit max width inherit但它改变了纵横比 parent border 1px solid width
  • 数据封装...?

    谁能向我解释一下 Objective C 中的数据封装是什么 有人告诉我这是 Objective C 的一个重要概念 但我不明白为什么 向我解释一下 就像我 5 岁一样 然后又好像我 25 岁一样 谢谢你的时间 丹尼尔 From http
  • 将日期格式代码转换为日期

    用户应该以以下格式输入日期 m d Y 我需要做的是将日期转换为 11 11 2013 这是今天的日期 我没有太多处理日期的工作 有没有某种方法可以开箱即用地进行这种转换 我查看了 DateTime 选项 但找不到我需要的内容 Edit 从
  • 这个错误是由于编译器对 RefCell 的特殊了解造成的吗?

    fn works lt a gt foo Option lt a mut String gt s a mut String fn error lt a gt foo RefCell