Box,ref和&之间的理解和关系和*

时间:2015-08-11 18:42:21

标签: pointers rust

我对Rust中指针的工作方式感到有些困惑。有refBox&*,我不确定它们是如何协同工作的。

以下是我目前的理解:

  1. Box实际上不是指针 - 它是一种在堆上分配数据的方法,并在函数参数中传递未经过类型化的(特别是特征)。
  2. ref用于模式匹配以借用您匹配的内容,而不是接受它。例如,

    let thing: Option<i32> = Some(4);
    match thing {
        None => println!("none!"),
        Some(ref x) => println!("{}", x), // x is a borrowed thing
    }
    println!("{}", x + 1); // wouldn't work without the ref since the block would have taken ownership of the data
    
  3. &用于进行借用(借用指针)。如果我有一个函数fn foo(&self),那么我会在函数终止后将自己的引用过期,只留下调用者的数据。我也可以通过bar(&mydata)传递我想要保留所有权的数据。

  4. *用于生成原始指针:例如let y: i32 = 4; let x = &y as *const i32。我理解C / C ++中的指针,但我不确定它如何与Rust的类型系统一起工作,以及如何安全地使用它们。我也不确定这种指针的用例是什么。此外,*符号可用于取消引用事物(什么东西,为什么?)。
  5. 有人可以向我解释第四种指针,并验证我对其他类型的理解是否正确?我也很感激任何人指出我没有提到的任何常见用例。

3 个答案:

答案 0 :(得分:83)

首先,你列出的所有项目都是不同的东西,即使它们与指针有关。 Box是库定义的智能指针类型; ref是模式匹配的语法; &是一个引用运算符,在引用类型中加倍作为sigil; *是一个解引用运算符,在原始指针类型中加倍作为sigil。请参阅下文以获取更多解释。

Rust中有四种基本指针类型,可以分为两组 - 引用和原始指针:

&T        - immutable (shared) reference
&mut T    - mutable (exclusive) reference

*const T  - immutable raw pointer
*mut T    - mutable raw pointer

最后两个之间的差异很小,因为任何一个都可以在没有任何限制的情况下强制转换为另一个,因此const / mut区别主要用作lint。原始指针可以自由地创建任何东西,例如,它们也可以通过整数来创建。

当然,引用并非如此 - 引用类型及其交互定义了Rust的一个关键特性:借用。参考文献对如何以及何时创建,如何使用以及它们如何相互交互有很多限制。作为回报,它们可以在没有unsafe块的情况下使用。然而,借用的确切内容及其运作方式超出了这个答案的范围。

可以使用&运算符创建引用和原始指针:

let x: u32 = 12;

let ref1: &u32 = &x;
let raw1: *const u32 = &x;

let ref2: &mut u32 = &mut x;
let raw2: *mut u32 = &mut x;

引用和原始指针都可以使用*运算符取消引用,但对于原始指针,它需要unsafe块:

*ref1; *ref2;

unsafe { *raw1; *raw2; }

取消引用运算符通常被省略,因为另一个运算符,&#34; dot&#34;运算符(即.)自动引用或取消引用其左参数。因此,例如,如果我们有这些定义:

struct X { n: u32 };

impl X {
    fn method(&self) -> u32 { self.n }
}

然后,尽管method()引用了selfself.n会自动解除引用,因此您不必输入(*self).n。调用method()时会发生类似的事情:

let x = X { n: 12 };
let n = x.method();

此处,编译器会自动引用x中的x.method(),因此您不必编写(&x).method()

最后一段代码也演示了特殊的&self语法。这仅表示self: &Self,或者更具体地说,在此示例中为self: &X&mut self*const self*mut self也有效。

因此,引用是Rust中的主要指针类型,应该几乎总是使用。原始指针(不具有引用限制)应该用于实现高级抽象(集合,智能指针等)和FFI(与C库交互)的低级代码中。

Rust也有dynamically-sized (or unsized) types。这些类型没有明确的静态已知大小,因此只能通过指针/引用使用。但是,只有指针是不够的 - 需要额外的信息,例如,切片的长度或指向特征对象的虚拟方法表的指针。这些信息是&#34;嵌入&#34;在指向未分类的类型的指针中,制作这些指针&#34; fat&#34;。

胖指针基本上是一个结构,它包含指向数据的实际指针和一些附加信息(切片的长度,指向特征对象的vtable的指针)。这里最重要的是Rust为用户绝对透明地处理指针内容的这些细节 - 如果你传递&[u32]*mut SomeTrait值,相应的内部信息将自动传递。

Box<T>是Rust标准库中的智能指针之一。它提供了一种在堆上分配足够内存以存储相应类型的值的方法,然后它用作句柄,指向该内存的指针。 Box<T>拥有它指向的数据;当它被删除时,堆上的相应内存被释放。

考虑框的一种非常有用的方法是将它们视为常规值,但具有固定大小。也就是说,Box<T>等同于T,除了它总是占用与机器指针大小相对应的字节数。我们说(拥有)框提供值语义。在内部,它们使用原始指针实现,就像几乎任何其他高级抽象一样。

Box es(事实上,对于几乎所有其他智能指针都是如此,例如Rc)也可以借用:你可以从{{&T中获得Box<T> 1}}。这可以通过.运算符自动执行,也可以通过取消引用并再次引用它来显式执行:

let x: Box<u32> = Box::new(12);
let y: &u32 = &*x;

在这方面,Box与内置指针类似 - 您可以使用取消引用运算符来访问其内容。这是可能的,因为Rust中的dereference运算符是可重载的,并且它对于大多数(如果不是全部)智能指针类型都是重载的。这样可以轻松借用这些指针内容。

最后,ref只是模式中的语法,用于获取引用类型的变量而不是值。例如:

let x: u32 = 12;

let y = x;           // y: u32, a copy of x
let ref z = x;       // z: &u32, points to x
let ref mut zz = x;  // zz: &mut u32, points to x

虽然上面的例子可以用引用运算符重写:

let z = &x;
let zz = &mut x;

(这也会使它更具惯用性),有些情况下ref是必不可少的,例如,在引用枚举变体时:

let x: Option<Vec<u32>> = ...;

match x {
    Some(ref v) => ...
    None => ...
}

在上面的示例中,x仅在整个match语句中借用,允许在x之后使用match。如果我们这样写:

match x {
    Some(v) => ...
    None => ...
}

然后此x将使用match,并且在此之后将无法使用。

答案 1 :(得分:9)

Box在逻辑上是原始指针(*const T)周围的新类型。但是,它在构造和销毁期间分配和释放其数据,因此不必从其他来源借用数据。

其他指针类型也是如此,例如Rc - 引用计数指针。这些是包含私有原始指针的结构,它们分配给它们并从中释放。

原始指针与普通指针具有完全相同的布局,因此在某些情况下与C指针不兼容。重要的是,*const str*const [T]胖指针,这意味着它们包含有关值的长度的额外信息。

然而,原始指针绝对不能保证其有效性。例如,我可以放心地做到

123 as *const String

此指针无效,因为内存位置123未指向有效的String。因此,当解除引用时,需要unsafe块。

此外,虽然借款必须遵守某些法律 - 即如果一个是可变的,你就不能有多次借款 - 原始指针不必尊重这一点。 There are other, weaker, laws that must be obeyed,但你不太可能与这些人发生冲突。

*mut*const之间没有逻辑差异,虽然它们可能需要转换为另一个来执行某些操作 - 差异是具有证据性的。

答案 2 :(得分:5)

引用和原始指针在实现级别是相同的。与程序员的观点不同的是,引用是安全的(在Rust术语中),但原始指针不是。

借用检查器保证引用始终有效(终身管理),您可以只有一个可变引用,等等。

这种类型的约束对于许多用例来说可能过于严格,因此原始指针(没有任何约束,如C / C ++)对于实现低级数据结构很有用,而且通常是低级别的东西。但是,您只能取消引用原始指针或在unsafe块内对它们执行操作。

标准库中的容器也是使用原始指针BoxRc实现的。

BoxRc是C ++中的智能指针,即原始指针的包装。