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

问题描述 投票:8回答:1

我正在写一些不安全的Rust代码,所以我需要知道*const T*mut T之间的确切差异。我认为它就像&T&mut T(即你不能通过T,期间改变&T),但似乎并非如此!

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

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

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

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:不变

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


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

pointers rust unsafe
1个回答
2
投票

Differences between *const T and *mut T

不可思议的是,mutable和const原始指针之间的主要区别是,取消引用它们是否会产生可变或不可变的位置表达式。取消引用一个const指针会产生一个不可变的place expression,取消引用一个可变指针会产生一个可变的指针。可变性according to the language reference的含义是这样的:

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

const和mutable指针之间的另一个区别是类型的方差,正如你已经注意到的那样,我认为就是这样。

Casting between mutable and const pointers

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

对于原始指针的可变性,Rust可以稍微放松一点的一个原因是,与引用相比,它不会对原始指针的别名做出任何假设。有关详细信息,请参阅What are the semantics for dereferencing raw pointers?

Why is NonNull using *const T?

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

Why does the language include two different kinds of pointers if they are so similar?

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.