为什么 std::cell::Ref 不使用引用而不是 NonNull?

问题描述 投票:0回答:1

Rust 中的

std::cell::Ref
结构体定义如下:

pub struct Ref<'b, T: ?Sized + 'b> {
    // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
    // `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
    // `NonNull` is also covariant over `T`, just like we would have with `&T`.
    value: NonNull<T>,
    borrow: BorrowRef<'b>,
}

// NB
注释(我假设Nota bene / Nasty Bug之类的?)意味着以下定义不起作用,因为这将是
noalias
违规(它们是指后端中的LLVM属性吗?):

pub struct Ref2<'b, T: ?Sized + 'b> {
    value: &'b T,
    borrow: BorrowRef<'b>,
}

我不明白这一点,因为我的印象是非词法生命周期语义在代码生成中被正确保留。否则下面的简单示例(当然可以编译)也是非法的,对吧?:

struct Foo<'a> {
    v: &'a i32,
}
fn foo(x: &mut i32) {
    let f = Foo { v: x };
    *x = 5; // value modified while the `noalias` f.v pointer is still in scope
}

对内部结构有更多了解的人可以为我解释一下吗? 我担心我在这里误解了一些关键的东西,导致我自己的不安全代码出现潜在问题。

rust language-lawyer unsafe refcell
1个回答
0
投票

非词法生命周期从来都不是代码生成的属性。它们纯粹是借用检查器属性,借用检查永远不会影响代码生成。

您提供的示例根据 LLVM 并不违法。

noalias
的问题仅在函数参数中体现,因为只有它们才获得
noalias
属性(至少目前如此)。因此,编写有问题的代码而没有不安全代码的唯一方法是:

fn foo(reference: &String, mut data: String) {
    // Do not use `reference` anymore here.
    
    // Write to data.
    data = String::new();
}

fn main() {
    let mut v = String::new();
    foo(&v, v);
}

除非它无法编译,因为你正在移动借来的

v
。所以实际上没有办法触发错误编译。

但是,有了

RefCell
,我们可以做到这一点:

use std::cell::{RefCell, Ref};

fn foo<'a>(reference: Ref<'a, i32>, data: &'a RefCell<i32>) {
    drop(reference);
    // Do not use `reference` anymore here.
    
    *data.borrow_mut() = 0;
}

fn main() {
    let v = RefCell::new(0);
    foo(v.borrow(), &v);
}

如果

Ref
使用引用,将会编译错误。

© www.soinside.com 2019 - 2024. All rights reserved.