首页 > 解决方案 > 为什么在同一范围内可以有多个具有静态生命周期的可变引用

问题描述

为什么我可以在同一范围内对静态类型有多个可变引用?

我的代码

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;
}

我知道借款规则

在上面的代码中,我有一个结构体,该结构体A带有get_b()返回可变引用的方法B。有了这个参考,我可以改变 struct 的字段B

奇怪的是,在同一个作用域()中可以创建多个可变引用b, b_1, b_2,我可以使用它们来修改B

为什么我可以有多个可变引用,其'static生命周期如图所示main()

我试图解释这种行为是因为我返回了一个具有'static生命周期的可变引用。每次我调用get_b()它都会返回相同的可变引用。最后,它只是一个相同的参考。这个想法对吗?为什么我能够使用get_b()单独获得的所有可变引用?

标签: staticrustlifetime

解决方案


这只有一个原因:你对编译器撒了谎。你在滥用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 开发人员的具体提醒:

放大 缩小,显示标志是 8+ 英尺高


推荐阅读