谁拥有堆中的一个盒子?

5

我现在正在学习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());
}

如何可视化此代码的堆栈和堆?
从我所学到的,prepend 拥有 list 的所有权,分配 heap 中的空间并将列表移动到 heap。当 prepend 完成后,它会将新创建的列表移动(转让所有权)给外部变量。
这种可视化是否正确?
首先,List::new 返回 Nil,因此堆栈将包含 Nil。
在执行 list.prepend(1) 后,Nil 将位于堆中的地址 0x0000(假设),堆栈将包含 Cons(1,0x0000)。
在执行 list.prepend(2) 后,Cons(1,0x0000) 将位于堆中的地址 0x00002(假设),堆栈将包含 Cons(2,0x0002)。
在执行 list.prepend(3) 后,Cons(2,0x0002) 将位于堆中的地址 0x00004(假设),堆栈将包含 Cons(3,0x0004)。
现在,谁拥有 Cons(1,0x0000) 的所有权?Cons(2,0x0002) 是否拥有 Cons(1,0x0000) 的所有权?堆中的变量是否允许拥有资源所有权?
从这段代码中可以看出,在堆中的变量可能拥有资源所有权,因此如果 Rust 反分配该变量,则会同时反分配资源。这是正确的吗?
1个回答

3

Box<Foo>代表一个在堆上某处被Box对象管理和拥有的Foo实例。

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

let list = Cons(3, Box::new(Cons(2, Box::new(Cons(1, Box::new(Nil))))))
  • list 拥有一个List对象,它是一个枚举的Cons变体,拥有一个值为3u32和一个Box<List>
  • 这个Box<List>管理和拥有一个List实例:一个Cons变体,拥有一个值为2和另一个Box<List>
  • 这第二个Box<List>管理和拥有一个List实例:一个Cons变体,拥有一个值为1和一个Box<List>
  • 这个最后的Box<List>管理和拥有一个List实例:一个Nil变体。

所以,是的,一个Box的内容可以拥有其他的Box,当一个Box被销毁时,它将适当地销毁其内容,期望其内容也能够适当地销毁其自身的内容,一直到拥有树的底部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接