当涉及原始指针时,Rust 如何推断生命周期?

Diz*_*tul 2 unsafe lifetime rust raw-pointer

struct MyCell<T> {
    value: T
}

impl<T> MyCell<T> {
    fn new(value: T) -> Self {
        MyCell { value }
    }
    
    fn get(&self) -> &T {
        &self.value
    }
    
    fn set(&self, new_value: T) {
        unsafe { 
            *(&self.value as *const T as *mut T) = new_value; 
        }
    }
}

fn set_to_local(cell: &MyCell<&i32>) {
    let local = 100;
    cell.set(&local);
}

fn main() {
    let cell = MyCell::new(&10);
    set_to_local(&cell);
}
Run Code Online (Sandbox Code Playgroud)

当调用时cell.set(&local),假设cellis'x'&localis ,我被告知协方差规则将更改from到 的'y类型。cell&MyCell<'x, &i32>&MyCell<'y, &i32>

unsafe 块内的赋值如何影响 的参数的生命周期推断set()?原始指针没有生命周期那么编译器如何知道它应该使用协方差cell具有new_value相同的生命周期?

Kev*_*eid 10

\n

unsafe 块内的赋值如何影响 的参数的生命周期推断set()

\n
\n

它不\xe2\x80\x94,这甚至不是特定于原始指针或unsafe. 函数体永远不会影响函数签名的任何方面(除了与async fn此处无关的 s )。

\n
\n

原始指针没有生命周期那么编译器如何知道它应该使用协方差cell具有new_value相同的生命周期?

\n
\n

听起来你误读了一些建议。在您的代码中,Cell<T>是 不变的T,但为了使内部可变类型健全,它在类型参数中必须是不变的(不是协变的)。在您的代码中,编译器推断 的 协方差T因为MyCell包含一个简单类型 的字段T。对于大多数泛型类型,协方差是 \xe2\x80\x9c典型\xe2\x80\x9d 情况。

\n

因此,您拥有的代码是不健全的,因为MyCell它是协变的T,但必须是不变的T

\n

您的代码也不健全,因为在 的实现中set(),您正在创建对T,的不可变引用&self.value,然后写入其指示对象。不管你怎么做,这都是\xe2\x80\x9c未定义的行为\xe2\x80\x9d ,因为&self.value向编译器/优化器创建断言,在引用被删除之前,指向的内存不会被修改。

\n

如果您想重新实现标准库Cell,则必须像标准库一样使用UnsafeCell原语进行操作:

\n
pub struct Cell<T: ?Sized> {\n    value: UnsafeCell<T>,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

UnsafeCell这就是您选择退出&不变性保证的方式:创建一个&UnsafeCell<T> 不断T不会被改变。它在 中也是不变的T,这会自动使包含类型在 中不变T。在这里,这两者都是必要的。

\n