谁拥有堆中的 Box?

kev*_*nyu 5 rust

我现在正在学习 Rust。我想检查一下我对 Rust 所有权的理解。我对递归结构中的所有权和借用概念感到困惑。我在rustbyexample.com 中看到了这段代码

// Allow Cons and Nil to be referred to without namespacing
use List::{Cons, Nil};

// A linked list node, which can take on any of these two variants
enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    Cons(u32, Box<List>),
    // Nil: A node that signifies the end of the linked list
    Nil,
}

// Methods can be attached to an enum
impl List {
    // Create an empty list
    fn new() -> List {
        // `Nil` has type `List`
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    fn prepend(self, elem: u32) -> List {
        // `Cons` also has type List
        Cons(elem, Box::new(self))
    }

    // Return the length of the list
    fn len(&self) -> u32 {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&List`, and `*self` has type `List`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    let mut list = List::new();

    // Append some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}
Run Code Online (Sandbox Code Playgroud)

如何可视化这段代码的堆栈和堆?

据我所知,prepend获取列表的所有权,在堆中分配空间并将列表移动到堆。当prepend完成后,它的动作(使所有权)新创建的列表,外部变量。

这个形象化正确吗?

First List::new 返回 Nil,因此堆栈将包含 Nil。

list.prepend(1) 执行后 Nil 将在地址 0x0000(假设)的堆中,堆栈将包含 Cons(1,0x0000)。

list.prepend(2) 执行后 Cons(1,0x0000) 将在堆中地址 0x00002(assumption),堆栈将包含 Cons(2,0x0002)。

list.prepend(3) 执行后 Cons(2,0x0002) 将在堆中地址 0x00004(assumption),堆栈将包含 Cons(3,0x0004)。

现在,谁拥有 Cons(1,0x0000) 的所有权?Cons(2,0x0002) 是否拥有 Cons(1,0x0000) 的所有权?堆中的变量是否允许拥有资源的所有权?

从这段代码中,我假设堆中的变量可能拥有资源的所有权,因此如果 Rust 释放该变量,它也会释放资源。这样对吗?

Lev*_*ans 3

Box<Foo>表示Foo堆上某处的实例,由该Box对象管理和拥有。

因此,在您的列表的情况下,最终值为:

let list = Cons(3, Box::new(Cons(2, Box::new(Cons(1, Box::new(Nil))))))
Run Code Online (Sandbox Code Playgroud)
  • list拥有一个List对象,该对象是Cons枚举的变体,拥有自身 au32的值3和 aBox<List>
  • Box<List>管理并拥有一个List实例:一个Cons变体拥有一个2值和另一个值Box<List>
  • 第二个Box<List>管理并拥有一个List实例:一个Cons变量,它自己拥有一个1值和一个Box<List>
  • 最后Box<List>一个管理并拥有一个List实例:一个Nil变体。

所以是的, a 的内容Box可能拥有其他Boxes ,并且当 aBox被销毁时,它将正确地销毁其内容,预计它本身也会正确地销毁其内容,直到拥有树的底部。