我是Rust的新手。当我阅读The Rust Programming Language的第15章时,我不知道为什么要在递归数据结构中使用Box
es而不是常规引用。该书的15.1解释说,为避免无限大小的结构,需要使用间接寻址,但没有解释为什么使用Box
。
#[derive(Debug)]
enum FunctionalList<'a> {
Cons(u32, &'a FunctionalList<'a>),
Nil,
}
use FunctionalList::{Cons, Nil};
fn main() {
let list = Cons(1, &Cons(2, &Cons(3, &Nil)));
println!("{:?}", list);
}
上面的代码编译并产生所需的输出。似乎使用FunctionalList
将少量数据存储在堆栈上效果很好。此代码会引起麻烦吗?
确实,在这种简单情况下FunctionalList
有效。但是,如果尝试以其他方式使用此结构,则会遇到一些困难。例如,假设我们尝试构造一个FunctionalList
,然后从一个函数返回它:
#[derive(Debug)]
enum FunctionalList<'a> {
Cons(u32, &'a FunctionalList<'a>),
Nil,
}
use FunctionalList::{Cons, Nil};
fn make_list(x: u32) -> FunctionalList {
return Cons(x, &Cons(x + 1, &Cons(x + 2, &Nil)));
}
fn main() {
let list = make_list(1);
println!("{:?}", list);
}
这将导致以下编译错误:
error[E0106]: missing lifetime specifier
--> src/main.rs:9:25
|
9 | fn make_list(x: u32) -> FunctionalList {
| ^^^^^^^^^^^^^^ help: consider giving it an explicit bounded or 'static lifetime: `FunctionalList + 'static`
如果遵循提示并添加'static
生存期,则会收到此错误:
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:10:12
|
10 | return Cons(x, &Cons(x + 1, &Cons(x + 2, &Nil)));
| ^^^^^^^^^^^^^^^^^^^^^^-----------------^^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
问题在于,内部FunctionalList
值由隐式临时变量拥有,它们的范围在make_list
函数的末尾结束。因此,这些值将在函数末尾删除,留下对它们的悬挂引用,Rust不允许这样做,因此借用检查器将拒绝此代码。
[相反,如果FunctionalList
被定义为其Box
组件的FunctionalList
,那么所有权将被从临时值移到包含的FunctionalList
中,而我们将能够在没有任何返回值的情况下返回它问题。
[使用原始的FunctionalList
,我们需要考虑的事情是Rust中的每个值都必须在某个地方拥有一个所有者;因此,如果在这种情况下FunctionaList
不是其内部FunctionalList
的所有者,则该所有权必须位于其他地方。在您的示例中,该所有者是一个隐式临时变量,但是在更复杂的情况下,我们可以使用其他类型的外部所有者。这是一个使用TypedArena
(来自typed-arena板条箱)拥有数据的示例,因此我们仍然可以实现make_list
函数的变体:
use typed_arena::Arena;
#[derive(Debug)]
enum FunctionalList<'a> {
Cons(u32, &'a FunctionalList<'a>),
Nil,
}
use FunctionalList::{Cons, Nil};
fn make_list<'a>(x: u32, arena: &'a Arena<FunctionalList<'a>>) -> &mut FunctionalList<'a> {
let l0 = arena.alloc(Nil);
let l1 = arena.alloc(Cons(x + 2, l0));
let l2 = arena.alloc(Cons(x + 1, l1));
let l3 = arena.alloc(Cons(x, l2));
return l3;
}
fn main() {
let arena = Arena::new();
let list = make_list(1, &arena);
println!("{:?}", list);
}
在这种情况下,我们修改了make_list
的返回类型,使其仅返回对FunctionalList
的可变引用,而不是返回拥有的FunctionalList
,因为现在所有权位于arena
中。