* const T和* mut T原始指针有什么区别?

Luk*_*odt 8 pointers unsafe rust

我正在编写一些不安全的Rust代码,因此我需要知道*const T和之间的确切区别*mut T。我以为是&T&mut T(即,您T不能通过&T,句号进行变异),但事实并非如此!

例如,指针包装器NonNull<T>的定义如下(source):

pub struct NonNull<T: ?Sized> {
    pointer: *const T,
}
Run Code Online (Sandbox Code Playgroud)

但是,可以*mut T通过as_ptr定义从以下包装器获取一个:

pub const fn as_ptr(self) -> *mut T {
    self.pointer as *mut T
}
Run Code Online (Sandbox Code Playgroud)

该功能甚至没有标记为unsafe!我不允许强制从转换&T&mut T(出于充分的理由!),但显然像这样强制转换指针是可以的。

该Nomicon提到了关于方差章*const T*mut T方差有所不同:

  • *const T:协变
  • *mut T:不变的

这是指针类型之间的唯一区别吗?在我看来,这很奇怪。


指针类型之间到底有什么区别?是否有限制,*const T*mut T没有?如果差异很小:在语言中同时包含两种指针类型的其他原因是什么?

Sve*_*ach 9

*const T和之间的区别*mut T

mutable 和 const 原始指针之间的主要区别在于,解引用它们是否会产生可变或不可变的位置表达式,这并不奇怪。取消引用一个 const 指针会产生一个不可变的位置表达式,取消引用一个可变指针会产生一个可变的位置表达式根据语言参考,可变性的含义是:

对于要分配给、可变借用、隐式可变借用或绑定到包含ref mut它的模式的位置表达式,它必须是可变的。

正如您已经指出的那样,常量指针和可变指针之间的另一个区别是类型的差异,我认为这就是全部。

可变指针和常量指针之间的转换

您可以在安全代码中将 a 强制转换*const T为 a *mut T,因为可变性的差异仅在您取消引用指针后才变得相关,而取消引用原始指针无论如何都是不安全的操作。如果不转换为可变指针,则无法获得 const 指针指向的内存的可变位置表达式。

与引用相比,Rust 对原始指针的可变性更加宽松的一个原因是,它没有对原始指针的别名做任何假设。请参阅取消引用原始指针的语义是什么?了解更多详情。

为什么在NonNull使用*const T

NonNull指针类型被用作构建块像智能指针BoxRc。这些类型公开遵循通常的 Rust 引用规则的接口——指针的变异只能通过智能指针本身的所有权或可变引用来实现,而对指针的共享引用只能通过借用智能指针本身来获得. 这意味着这些类型协变是安全的,这只有在协变时才有可能NonNull,这意味着我们需要使用 a*const T而不是 a *mut T

如果语言如此相似,为什么要包含两种不同类型的指针?

让我们考虑一下替代方案。如果只有一个指针类型,它必然需要是可变指针——否则我们将无法通过原始指针修改任何内容。但是该指针类型也需要是协变的,否则我们将无法构建协变智能指针类型。(总是可以通过PhantomData<some invariant type>在结构中包含 a 来放弃协变,但是一旦您的结构被其成员之一呈现为不变,就无法再次使其协变。)由于可变引用是不变的,因此这个虚构的行为指针类型会有些令人惊讶。

另一方面,有两种不同的指针类型可以很好地类比引用:const 指针是协变的,并且解引用到不可变的位置表达式,就像共享引用一样,可变指针是不变的并且解引用到可变的位置表达式,就像可变的一样参考。

我只能推测这些是否是语言设计的实际原因,因为我找不到关于该主题的任何讨论,但这个决定对我来说似乎并不合理。