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
}
对内部结构有更多了解的人可以为我解释一下吗? 我担心我在这里误解了一些关键的东西,导致我自己的不安全代码出现潜在问题。
非词法生命周期从来都不是代码生成的属性。它们纯粹是借用检查器属性,借用检查永远不会影响代码生成。
您提供的示例根据 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
使用引用,将会编译错误。