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

时间:2019-04-13 11:17:15

标签: pointers rust unsafe

我正在编写一些不安全的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:不变
  •   

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


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

1 个答案:

答案 0 :(得分:2)

*const T*mut T之间的差异

可变和const原始指针之间的主要区别不足为奇,即对它们的取消引用会产生可变的还是不变的位置表达式。取消引用const指针会产生不可变的place expression,取消引用可变的指针会产生可变的。可变性according to the language reference的含义是:

  

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

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

在可变指针和const指针之间进行投射

您可以用安全代码将*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<some invariant type>来放弃协方差,但是一旦您的结构被其成员之一变为不变,就无法再次使其协变。)由于可变引用是不变的,这种虚构的指针类型的行为会有些令人惊讶。

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

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