gpt4 book ai didi

pointers - Box、ref、&和*的理解和关系

转载 作者:行者123 更新时间:2023-11-29 07:40:12 25 4
gpt4 key购买 nike

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

以下是我目前的理解:

  • Box 并不是真正的指针——它是一种在堆上分配数据的方法,并在函数参数中传递无大小的类型(尤其是特征)。
  • 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
  • & 用于进行借位(借用指针)。如果我有一个函数 fn foo(&self),那么我将引用我自己,该函数将在函数终止后过期,而只留下调用者的数据。我还可以通过执行 bar(&mydata) 传递我想保留所有权的数据。
  • * 用于制作原始指针:例如, let y: i32 = 4; let x = &y as *const i32 。我理解 C/C++ 中的指针,但我不确定它如何与 Rust 的类型系统一起工作,以及如何安全地使用它们。我也不确定这种类型的指针的用例是什么。此外,* 符号可用于取消引用事物(什么事物,为什么?)。

  • 有人可以向我解释第 4 种类型的指针,并验证我对其他类型的理解是否正确?我也很感激有人指出我没有提到的任何常见用例。

    最佳答案

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

    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; }

    解引用运算符经常被省略,因为另一个运算符“点”运算符(即 . )会自动引用或解引用其左参数。因此,例如,如果我们有这些定义:
    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 。这些类型没有明确的静态已知大小,因此只能通过指针/引用使用。然而,只有一个指针是不够的——还需要额外的信息,例如,切片的长度或指向 trait 对象的虚拟方法表的指针。此信息“嵌入”在指向未调整大小的类型的指针中,使这些指针变得“胖”。

    胖指针基本上是一个结构,它包含指向数据块的实际指针和一些附加信息(切片的长度,特征对象的 vtable 指针)。这里重要的是 Rust 处理这些关于指针内容的细节对用户绝对透明——如果你传递 &[u32]*mut SomeTrait 值,相应的内部信息将自动传递。
    Box<T> 是 Rust 标准库中的智能指针之一。它提供了一种在堆上分配足够内存以存储相应类型的值的方法,然后它充当句柄,即指向该内存的指针。 Box<T> 拥有它指向的数据;当它被删除时,堆上相应的内存块被释放。

    考虑框的一种非常有用的方法是将它们视为常规值,但具有固定大小。也就是说, Box<T> 等价于 T ,除了它总是占用与您机器的指针大小相对应的字节数。我们说(拥有的)盒子提供了值(value)语义。在内部,它们是使用原始指针实现的,就像几乎所有其他高级抽象一样。
    Box es(实际上,对于几乎所有其他智能指针都是如此,例如 Rc )也可以借用:您可以从 &T 中获得 Box<T> 。这可以使用 . 运算符自动发生,或者您可以通过取消引用并再次引用它来显式执行此操作:
    let x: Box<u32> = Box::new(12);
    let y: &u32 = &*x;

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

    最后, 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 消耗,并且在它之后将变得无法使用。

    关于pointers - Box、ref、&和*的理解和关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31949579/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com