了解 Rust 借用和解除引用

Liv*_*viu 6 rust

我正在阅读 Rust 文档,但似乎无法完全理解正在发生的事情。例如,在这里我看到以下示例:

// This function takes ownership of a box and destroys it
fn eat_box_i32(boxed_i32: Box<i32>) {
    println!("Destroying box that contains {}", boxed_i32);
}

// This function borrows an i32
fn borrow_i32(borrowed_i32: &i32) {
    println!("This int is: {}", borrowed_i32);
}

fn main() {
    // Create a boxed i32, and a stacked i32
    let boxed_i32 = Box::new(5_i32);
    let stacked_i32 = 6_i32;

    // Borrow the contents of the box. Ownership is not taken,
    // so the contents can be borrowed again.
    borrow_i32(&boxed_i32);
    borrow_i32(&stacked_i32);

    {
        // Take a reference to the data contained inside the box
        let _ref_to_i32: &i32 = &boxed_i32;

        // Error!
        // Can't destroy `boxed_i32` while the inner value is borrowed later in scope.
        eat_box_i32(boxed_i32);
        // FIXME ^ Comment out this line

        // Attempt to borrow `_ref_to_i32` after inner value is destroyed
        borrow_i32(_ref_to_i32);
        // `_ref_to_i32` goes out of scope and is no longer borrowed.
    }

    // `boxed_i32` can now give up ownership to `eat_box` and be destroyed
    eat_box_i32(boxed_i32);
}

Run Code Online (Sandbox Code Playgroud)

我相信的事情:

  1. eat_box_i32 接受一个指向 Box 的指针
  2. 此行let boxed_i32 = Box::new(5_i32);使得 boxed_i32 现在包含一个指针,因为 Box 不是基元

我不明白的事情:

  1. 为什么我们需要用borrow_i32(&boxed_i32);&符号来调用?boxed_i32 不是已经是一个指针了吗?
  2. 在这一行上:let _ref_to_i32: &i32 = &boxed_i32;为什么右侧需要&符号?boxed_i32 不是已经是一个地址了吗?
  3. 为什么可以使用指向 Box 的指针和指向 i32 的指针来调用borrow_i32?

mat*_*ots 15

对术语“指针”的评论

如果您愿意,可以跳过这一部分,我只是想考虑到您提出的问题,这可能是一个有用的评论:

在 Rust 中,&i32, &mut i32, *const i32, *mut i32, Box<i32>, Rc<i32>,Arc<i32>都可以说是“指向i32”类型的指针。然而,Rust 不会让你随意在它们之间进行转换,即使是在内存中布局相同的那些之间。

有时谈论一般指针可能很有用,但根据经验,如果您试图弄清楚为什么一段 Rust 代码可以编译,而另一段则不能,我建议跟踪哪一种您正在使用的指针。


你相信的事情:

  1. eat_box_i32 接受一个指向 Box 的指针

其实不完全是。eat_box_i32接受 a Box<i32>,而不是指向 a 的指针Box<i32>。碰巧Box<i32>在内存中存储为指向i32.

  1. 这一行让 boxed_i32 = Box::new(5_i32); 使得 boxed_i32 现在包含一个指针,因为 Box 不是基元

是的,boxed_i32是一个指针。


你不明白的事情:

  1. 为什么我们需要调用borrow_i32(&boxed_i32); 与&符号?boxed_i32 不是已经是一个指针了吗?

是的,boxed_i32已经是一个指针了。然而,装箱指针仍然表示所有权。如果你传递boxed_i32而不是&boxed_i32,你仍然会传递一个指针,但 Rust 会认为该变量“已消耗”,并且在该函数调用之后你将无法再使用boxed_i32。

  1. 在这一行: let _ref_to_i32: &i32 = &boxed_i32; 为什么右侧需要 & 符号?boxed_i32 不是已经是一个地址了吗?

是的,boxed_i32已经是一个地址,但事实上它是一个地址,这意味着它是不透明的(就像具有struct单个私有字段的地址一样)。的实际类型&boxed_i32是&Box<i32>.

虽然这很奇怪吧?如果&boxed_i32是&Box<i32>,如何将其分配给类型变量&i32?

这实际上是一种简写——如果一个类型T实现了该Deref<Target=R>特征,它会根据需要自动将类型的值转换&T为类型的值&R。事实证明该Box<T>类型实现了Deref<Target=T>.

有关 的更多信息,请参阅https://doc.rust-lang.org/std/ops/trait.Deref.htmlDeref。

因此,如果您在没有自动转换的情况下显式地写出它,那么该行实际上看起来像这样:

let _ref_to_i32: &i32 = Deref::deref(&boxed_i32);
Run Code Online (Sandbox Code Playgroud)
  1. 为什么可以使用指向 Box 的指针和指向 i32 的指针来调用borrow_i32?

其理由与上述(2)相同。

borrow_i32接受&i32作为其参数。通过&i32显然是可以的,因为类型完全匹配。如果您尝试传递它&Box<i32>,Rust 会自动将其转换为&i32您的,因为Box<i32>实现了Deref<i32>.


编辑:感谢@kmdreko指出Deref允许强制,而不是AsRef