我正在写一些不安全的防 rust 代码,所以我需要知道*const T*mut T之间的确切区别.我假设它是&T&mut T(也就是说,你不能从T&T变异,句号),但事实似乎并非如此!

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

pub struct NonNull<T: ?Sized> {
    pointer: *const T,
}

但是,可以通过as_ptr从这个包装器获得*mut T,它的定义如下:

pub const fn as_ptr(self) -> *mut T {
    self.pointer as *mut T
}

该函数甚至没有标记为unsafe!我不允许从&T投到&mut T(这是一个很好的理由!),但很明显,像这样投球是可以的.

Nomicon在the chapter about variance中提到*const T*mut T的方差不同:

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

这是指针类型之间唯一的区别吗?这对我来说似乎很奇怪...


What exactly are the differences between the pointer types? *mut T没有*const T的限制吗?如果差异很小:在语言中包含这两种指针类型的其他原因是什么?

推荐答案

Differences between *const T and *mut T

mutable和const raw指针之间的主要区别是,不出意料地,取消对它们的引用是否会产生一个可变或不可变的位置表达式.取消对常量指针的引用会产生一个不可变的place expression,取消对可变指针的引用会产生一个可变的place expression.可变性according to the language reference的含义如下:

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

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

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

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

Rust对原始指针的可变性更为宽松的一个原因是,与引用相比,它没有对原始指针的别名做出任何假设.详见What are the semantics for dereferencing raw pointers?.

Why is NonNull using *const T?

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

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

让我们考虑另一种 Select .如果只有一个指针类型,它就必须是可变指针——否则我们将无法通过原始指针修改任何内容.但指针类型也需要是协变的,因为否则我们就无法构建协变的智能指针类型.(总是可以通过在 struct 中包含PhantomData<some invariant type>来放弃协方差,但一旦 struct 的一个成员使其保持不变,就无法再次使其协方差.)由于可变引用是不变的,因此这种假想指针类型的行为会有点令人惊讶.

另一方面,有两种不同的指针类型,可以很好地类比引用:常量指针是协变的,与共享引用一样,是对不可变位置表达式的解引用;可变指针是不变的,与可变位置表达式一样,是对不可变位置表达式的解引用,与可变引用一样.

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

Rust相关问答推荐

为什么Tauri要修改被调用函数的参数名称?

什么是谓词的简短和简洁类型

重新导出proc宏导致未解决的extern crate错误""

访问Rust中的隐藏变量

我怎样才能从一个Rust 的日期中go 掉3年?

什么是Rust惯用的方式来使特征向量具有单个向量项的别名?

如何使用syn插入 comments ?

为什么允许我们将可变引用转换为不可变引用?

有没有一种惯用的方法来判断VEC中是否存在变体?

在Rust中,Box:ed struct 与普通 struct 在删除顺序上有区别吗?

期望一个具有固定大小 x 元素的数组,找到一个具有 y 元素的数组

Rust:为什么 Pin 必须持有指针?

注释闭包参数强调使用高阶排定特征界限

是否可以通过可变引用推进可变切片?

在运行时在 Rust 中加载字体

将 Futures 的生命周期特征绑定到 fn 参数

是否可以在 Rust 中的特定字符上实现特征?

n 个范围的笛卡尔积

字符串切片的向量超出范围但原始字符串仍然存在,为什么判断器说有错误?

在 Rust 中退出进程