当涉及原始指针时,Rust 如何推断生命周期?
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);
}
当调用cell.set(&local)
时,假设cell
为'x
,'&local
为'y
,我被告知协方差规则会将 cell
的类型从 &MyCell<'x, &i32>
更改为&MyCell<'y, &i32>
。
unsafe 块内的赋值如何影响 set()
参数的生命周期推断?原始指针没有生命周期,那么编译器如何知道它应该使用协方差使 cell
和 new_value
具有相同的生命周期?
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);
}
When calling cell.set(&local)
, suppose cell
is 'x
and '&local
is 'y
, I am told that the covariance rule will change the type of cell
from &MyCell<'x, &i32>
to &MyCell<'y, &i32>
.
How does the assignment inside the unsafe block affect the lifetime inference for the parameters of set()
? Raw pointers does not have lifetime then how does the compiler know it should make cell
and new_value
have the same lifetime using covariance?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
事实并非如此——这甚至不是特定于原始指针或
不安全
。函数体永远不会影响函数签名的任何方面(除了这里不相关的async fn
)。听起来你误读了一些建议。在您的代码中,
Cell
在T
中是不变的,但要使内部可变类型健全,它必须是不变 (非协变)在类型参数中。在您的代码中,编译器会推断T
的协方差,因为MyCell
包含一个简单类型T
的字段。协方差是大多数泛型类型的“典型”情况。因此,您的代码不健全,因为
MyCell
在T
上是协变的,但必须在T
上保持不变。您的代码也不健全,因为在
set()
的实现中,您正在创建对T
、&的不可变引用;self.value
,然后写入其所指对象。无论您如何执行,这都是“未定义行为”,因为创建&self.value
向编译器/优化器断言指向的内存不会被修改直到引用被删除。如果你想重新实现标准库的
Cell
,你必须像标准库那样做,使用UnsafeCell
原语:UnsafeCell
是您选择退出&
的不变性保证的方式:创建一个&UnsafeCell< /code> doesn't 断言
T
不会发生突变。它在T
中也是不变的,这会自动使包含类型在T
中不变。在这里,这两者都是必要的。It doesn't — and this is not even specific to raw pointers or
unsafe
. Function bodies never affect any aspect of the function signature (except forasync fn
s which are irrelevant here).It sounds like you misread some advice. In the code you have,
Cell<T>
is invariant inT
, but for an interior-mutable type to be sound, it must be invariant (not covariant) in the type parameter. In the code you have, the compiler infers covariance forT
becauseMyCell
contains a field that is simply of the typeT
. Covariance is the “typical” case for most generic types.Therefore, the code you have is unsound because
MyCell
is covariant overT
but must instead be invariant overT
.Your code is also unsound because in the implementation of
set()
, you're creating an immutable reference to aT
,&self.value
, and then writing to its referent. This is “undefined behavior” no matter how you do it, because creating&self.value
asserts to the compiler/optimizer that the pointed-to memory won't be modified until the reference is dropped.If you want to reimplement the standard library's
Cell
, you must do it like the standard library does, with theUnsafeCell
primitive:UnsafeCell
is how you opt out of&
's immutability guarantees: creating an&UnsafeCell<T>
doesn't assert that theT
won't be mutated. It is also invariant inT
, which automatically makes the containing type invariant inT
. Both of these are necessary here.