我现在正在学习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 反分配该变量,则会同时反分配资源。这是正确的吗?