static - 为什么在同一范围内可以有多个具有静态生命周期的可变引用
问题描述
为什么我可以在同一范围内对静态类型有多个可变引用?
我的代码:
static mut CURSOR: Option<B> = None;
struct B {
pub field: u16,
}
impl B {
pub fn new(value: u16) -> B {
B { field: value }
}
}
struct A;
impl A {
pub fn get_b(&mut self) -> &'static mut B {
unsafe {
match CURSOR {
Some(ref mut cursor) => cursor,
None => {
CURSOR= Some(B::new(10));
self.get_b()
}
}
}
}
}
fn main() {
// first creation of A, get a mutable reference to b and change its field.
let mut a = A {};
let mut b = a.get_b();
b.field = 15;
println!("{}", b.field);
// second creation of A, a the mutable reference to b and change its field.
let mut a_1 = A {};
let mut b_1 = a_1.get_b();
b_1.field = 16;
println!("{}", b_1.field);
// Third creation of A, get a mutable reference to b and change its field.
let mut a_2 = A {};
let b_2 = a_2.get_b();
b_2.field = 17;
println!("{}", b_1.field);
// now I can change them all
b.field = 1;
b_1.field = 2;
b_2.field = 3;
}
我知道借款规则
- 一个或多个对资源的引用 (
&T
), - 正是一个可变引用 (
&mut T
)。
在上面的代码中,我有一个结构体,该结构体A
带有get_b()
返回可变引用的方法B
。有了这个参考,我可以改变 struct 的字段B
。
奇怪的是,在同一个作用域()中可以创建多个可变引用b, b_1, b_2
,我可以使用它们来修改B
。
为什么我可以有多个可变引用,其'static
生命周期如图所示main()
?
我试图解释这种行为是因为我返回了一个具有'static
生命周期的可变引用。每次我调用get_b()
它都会返回相同的可变引用。最后,它只是一个相同的参考。这个想法对吗?为什么我能够使用get_b()
单独获得的所有可变引用?
解决方案
这只有一个原因:你对编译器撒了谎。你在滥用unsafe
代码并且违反了 Rust 关于可变别名的核心原则。你说你知道借用规则,但你却想方设法打破它们!
unsafe
代码为您提供了一小组额外的能力,但作为交换,您现在有责任避免所有可能的未定义行为。多个可变别名是未定义的行为。
涉及的事实static
与问题完全正交。您可以在您关心的任何生命周期内创建对任何东西(或什么都没有)的多个可变引用:
fn foo() -> (&'static i32, &'static i32, &'static i32) {
let somewhere = 0x42 as *mut i32;
unsafe { (&*somewhere, &*somewhere, &*somewhere) }
}
在您的原始代码中,您声明任何人都可以安全地调用get_b
任意次数。这不是真的。应将整个功能标记为不安全,并附上关于允许和不允许什么的大量文档,以防止触发不安全。任何unsafe
块都应该有相应的注释来解释为什么特定的用法不会违反所需的规则。所有这些都使得创建和使用unsafe
代码比安全代码更乏味,但与每行代码在概念上unsafe
的 C 相比,它仍然要好得多。
unsafe
只有当你比编译器更了解时,你才应该使用代码。在大多数情况下,对于大多数人来说,几乎没有理由创建unsafe
代码。
来自Firefox 开发人员的具体提醒:
推荐阅读
- ios - 添加 Webviews 后 React-Native Native 模块不能为空
- json - 如何将单独的 json 对象列表合并到一个对象数组中?
- angularjs - 关闭应用程序时 Ionic Firebase 数据更改
- mysql - 使用python从目录上传所有文件
- android - 在房间持久性库中使任何列自动生成(不是主键)
- arrays - 如何将数组值附加到laravel中的集合中?
- android - 是否还有任何受支持的 Eclipse 应用程序\版本?(用于维护旧项目)
- ios - TrueDepth 相机前景分割,改善遮罩效果
- json - 将 JSON 文件转换为 TFRecord 时出现错误
- ruby-on-rails - 在使用 minimagick 创建的 Ruby 中编辑 PDF 的元数据