`*const T` 和 *mut T` 原始指针有啥区别?

Posted

技术标签:

【中文标题】`*const T` 和 *mut T` 原始指针有啥区别?【英文标题】:What are the differences between `*const T` and *mut T` raw pointers?`*const T` 和 *mut T` 原始指针有什么区别? 【发布时间】:2019-09-04 00:00:45 【问题描述】:

我正在编写一些不安全的 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!我不允许从&amp;T 转换为&amp;mut T(有充分的理由!),但显然这样的转换指针很好。

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

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

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


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

【问题讨论】:

What are the semantics for dereferencing raw pointers? 回答你的问题了吗? @trentcl 这很有帮助,谢谢!但我不认为它完全回答了我的问题。一方面,这个答案根本没有提到差异(我肯定知道一件事在这里是相关的)。我觉得您链接的答案绝对应该与回答此问题的答案相关联。但是,是的,我认为它不能完全回答这个问题。 一方面,您不能分配给*const T 的取消引用。 【参考方案1】:

*const T*mut T 之间的差异

mutable 和 const 原始指针之间的主要区别毫不奇怪,取消引用它们会产生可变或不可变的位置表达式。取消引用 const 指针会产生不可变的place expression,取消引用可变指针会产生可变指针。可变性according to the language reference 的含义如下:

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

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

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

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

Rust 可以对原始指针的可变性更加放松的一个原因是,与引用相比,它不对原始指针的别名做出任何假设。详情请参阅What are the semantics for dereferencing raw pointers?。

为什么NonNull 使用*const T

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

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

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

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

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

【讨论】:

以上是关于`*const T` 和 *mut T` 原始指针有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该传递“T* const&”类型的指针?

使用 const 引用删除引用

如何将 Rust `Vec<T>` 暴露给 FFI?

指针指针的 const 问题

C++中四种类型转换符:static_castdynamic_castreinterpret_cast和const_cast要点解析

致命错误:从未对齐的原始指针加载