【问题标题】:Understanding rust borrowing and dereferencing了解 rust 借用和取消引用
【发布时间】:2020-09-17 18:45:59
【问题描述】:

我正在阅读 Rust 文档,但似乎无法完全理解正在发生的事情。比如在here上面我看到了下面的例子:

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

我相信的事情:

  1. eat_box_i32 采用指向 Box 的指针
  2. 这一行 let boxed_i32 = Box::new(5_i32); 使得 boxed_i32 现在包含一个指针,因为 Box 不是原语

我不明白的事情:

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    对术语“指针”的评论

    如果你愿意,你可以跳过这部分,我只是根据你提出的问题来计算,这可能是一个有用的评论:

    在 Rust 中,&amp;i32&amp;mut i32*const i32*mut i32Box&lt;i32&gt;Rc&lt;i32&gt;Arc&lt;i32&gt; 都可以说是“指向i32”类型的指针。但是,Rust 不会让您随意在它们之间进行转换,即使在内存中布局相同的那些之间也是如此。

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


    你相信的事情:

    1. eat_box_i32 采用指向 Box 的指针

    其实不完全。 eat_box_i32 接受 Box&lt;i32&gt;,而不是指向 Box&lt;i32&gt; 的指针。恰好Box&lt;i32&gt;在内存中被存储为指向i32的指针。

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

    是的,boxed_i32 是一个指针。


    你不明白的地方:

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

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

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

    是的,boxed_i32 已经是一个地址,但事实上它是一个地址是不透明的(就像一个带有单个私有字段的 struct)。 &amp;boxed_i32 的实际类型是&amp;Box&lt;i32&gt;

    虽然这很奇怪,对吧?如果&amp;boxed_i32&amp;Box&lt;i32&gt;,如何将其分配给&amp;i32 类型的变量?

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

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

    因此,如果您在没有自动转换的情况下明确地写出来,那行实际上看起来像:

    let _ref_to_i32: &i32 = Deref::deref(&boxed_i32);
    
    1. 怎么会用指向 Box 的指针和指向 i32 的指针来调用 borrow_i32?

    原因同上(2)。

    borrow_i32 接受 &amp;i32 作为其参数。传递&amp;i32 显然没问题,因为类型完全匹配。如果你尝试传递&amp;Box&lt;i32&gt;,Rust 会自动为你将其转换为&amp;i32,因为Box&lt;i32&gt; 实现了Deref&lt;i32&gt;


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

    【讨论】:

      【解决方案2】:

      作为对@math4tots 的补充,自动取消引用称为 Deref Coercion。这里的 rustbook 中有解释:https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods

      【讨论】:

        猜你喜欢
        • 2019-02-28
        • 1970-01-01
        • 1970-01-01
        • 2022-12-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-09
        • 2022-11-02
        • 2018-01-12
        相关资源
        最近更新 更多