这个练习介绍的是用 Box 实现递归类型。
问题:递归类型大小无限
原来的定义是:
enum List {
Cons(i32, List),
Nil,
}
Cons 包含一个 List,而这个 List 又可以是 Cons,继续包含另一个 List:
Cons(i32, Cons(i32, Cons(i32, ...)))
因此,Rust 无法在编译时确定 List 的大小。
解决方案:使用 Box
Box<T> 本身的大小是固定的,因为它只保存一个指向堆内存的指针。真正的数据存储在堆上。
修改为:
enum List {
Cons(i32, Box<List>),
Nil,
}
此时 List 的大小是可以确定的:
Nil 不包含数据
Cons 包含一个 i32 和一个固定大小的 Box<List>
完整实现
#[derive(PartialEq, Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
fn create_empty_list() -> List {
List::Nil
}
fn create_non_empty_list() -> List {
List::Cons(1, Box::new(List::Nil))
}
fn main() {
println!("This is an empty cons list: {:?}", create_empty_list());
println!(
"This is a non-empty cons list: {:?}",
create_non_empty_list(),
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_empty_list() {
assert_eq!(create_empty_list(), List::Nil);
}
#[test]
fn test_create_non_empty_list() {
assert_ne!(create_empty_list(), create_non_empty_list());
}
}
Cons 的含义
List::Cons(1, Box::new(List::Nil))
表示:
1 -> Nil
如果要创建更长的链表:
let list = List::Cons(
1,
Box::new(List::Cons(
2,
Box::new(List::Cons(
3,
Box::new(List::Nil),
)),
)),
);
它表示:
1 -> 2 -> 3 -> Nil
为什么使用 Box::new
Box::new(List::Nil) 会:
- 创建一个
List::Nil
- 将它放到堆上
- 返回一个
Box<List> 指针
所以 Cons 的第二个字段符合定义:
Cons(i32, Box<List>)
另外,#[derive(PartialEq, Debug)] 让这个枚举支持:
Debug:使用 {:?} 打印
PartialEq:使用 == 和 != 比较
其中 Box<List> 也会自动比较它指向的 List 内容。