`*const T` 和 *mut T` 原始指针有什么区别?
What are the differences between `*const T` and *mut T` raw pointers?
我正在编写一些不安全的 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
: covariant
*mut T
: invariant
这是指针类型之间的唯一区别吗?这对我来说似乎很奇怪...
指针类型之间到底有什么区别? *const T
是否有 *mut T
没有的限制?如果差异很小:在语言中包含这两种指针类型的其他原因是什么?
*const T
和 *mut T
之间的差异
毫不奇怪,可变原始指针和常量原始指针之间的主要区别在于,取消引用它们会产生可变还是不可变的位置表达式。取消引用 const 指针会产生不可变的 place expression, dereferencing a mutable pointer yields a mutable one. The implications of mutability according to the language reference 是这样的:
For a place expression to be assigned to, mutably borrowed, implicitly mutably borrowed, or bound to a pattern containing ref mut
it must be mutable.
常量指针和可变指针之间的另一个区别是类型的差异,正如您已经指出的,我认为仅此而已。
可变指针和常量指针之间的转换
您可以在安全代码中将 *const T
转换为 *mut T
,因为可变性的差异只有在您取消引用指针后才变得相关,并且取消引用原始指针无论如何都是不安全的操作。如果不强制转换为可变指针,则无法为 const 指针指向的内存获取可变位置表达式。
Rust 可以对原始指针的可变性放宽一点的一个原因是,与引用相比,它不会对原始指针的别名做出任何假设。有关详细信息,请参阅 。
为什么 NonNull
使用 *const T
?
NonNull
指针类型用作 Box
和 Rc
等智能指针的构建块。这些类型公开了遵循通常的 Rust 引用规则的接口——指针对象的变异只能通过智能指针本身的所有权或可变引用来实现,并且指针对象的共享引用只能通过借用智能指针本身来获得.这意味着这些类型是协变的是安全的,这只有在 NonNull
是协变的情况下才有可能,这反过来意味着我们需要使用 *const T
而不是 *mut T
.
为什么语言包含两种不同类型的指针,如果它们如此相似?
让我们考虑一下替代方案。如果只有一个指针类型,它必然需要是可变指针——否则我们将无法通过原始指针修改任何内容。但是该指针类型也需要是协变的,否则我们将无法构建协变智能指针类型。 (总是可以通过在结构中包含 PhantomData<some invariant type>
来放弃协变性,但是一旦您的结构被其成员之一呈现为不变性,就无法使其再次协变。)由于可变引用是不变的,这种假想的指针类型的行为有点令人惊讶。
另一方面,拥有两种不同的指针类型可以很好地类比引用:const 指针是协变的并且解引用到不可变的位置表达式,就像共享引用一样,可变指针是不变的并且解引用到可变的位置表达式,就像可变引用一样。
我只能推测这些是否是语言设计的实际原因,因为我找不到关于该主题的任何讨论,但这个决定对我来说似乎并非不合理。
我正在编写一些不安全的 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
: covariant*mut T
: invariant
这是指针类型之间的唯一区别吗?这对我来说似乎很奇怪...
指针类型之间到底有什么区别? *const T
是否有 *mut T
没有的限制?如果差异很小:在语言中包含这两种指针类型的其他原因是什么?
*const T
和 *mut T
之间的差异
毫不奇怪,可变原始指针和常量原始指针之间的主要区别在于,取消引用它们会产生可变还是不可变的位置表达式。取消引用 const 指针会产生不可变的 place expression, dereferencing a mutable pointer yields a mutable one. The implications of mutability according to the language reference 是这样的:
For a place expression to be assigned to, mutably borrowed, implicitly mutably borrowed, or bound to a pattern containing
ref mut
it must be mutable.
常量指针和可变指针之间的另一个区别是类型的差异,正如您已经指出的,我认为仅此而已。
可变指针和常量指针之间的转换
您可以在安全代码中将 *const T
转换为 *mut T
,因为可变性的差异只有在您取消引用指针后才变得相关,并且取消引用原始指针无论如何都是不安全的操作。如果不强制转换为可变指针,则无法为 const 指针指向的内存获取可变位置表达式。
Rust 可以对原始指针的可变性放宽一点的一个原因是,与引用相比,它不会对原始指针的别名做出任何假设。有关详细信息,请参阅
为什么 NonNull
使用 *const T
?
NonNull
指针类型用作 Box
和 Rc
等智能指针的构建块。这些类型公开了遵循通常的 Rust 引用规则的接口——指针对象的变异只能通过智能指针本身的所有权或可变引用来实现,并且指针对象的共享引用只能通过借用智能指针本身来获得.这意味着这些类型是协变的是安全的,这只有在 NonNull
是协变的情况下才有可能,这反过来意味着我们需要使用 *const T
而不是 *mut T
.
为什么语言包含两种不同类型的指针,如果它们如此相似?
让我们考虑一下替代方案。如果只有一个指针类型,它必然需要是可变指针——否则我们将无法通过原始指针修改任何内容。但是该指针类型也需要是协变的,否则我们将无法构建协变智能指针类型。 (总是可以通过在结构中包含 PhantomData<some invariant type>
来放弃协变性,但是一旦您的结构被其成员之一呈现为不变性,就无法使其再次协变。)由于可变引用是不变的,这种假想的指针类型的行为有点令人惊讶。
另一方面,拥有两种不同的指针类型可以很好地类比引用:const 指针是协变的并且解引用到不可变的位置表达式,就像共享引用一样,可变指针是不变的并且解引用到可变的位置表达式,就像可变引用一样。
我只能推测这些是否是语言设计的实际原因,因为我找不到关于该主题的任何讨论,但这个决定对我来说似乎并非不合理。