我认为你错过了 Rust 的一些关键细节;我认为我们需要处理三件事:
- 模式如何运作;
- 不可变(
&
) 和可变 (&mut
) 参考;
- Rust 的所有权模型如何运作(因为你
&*box
尝试)。
我将首先处理模式部分;在fn add(mut list: &List, x: uint)
,使用了两种模式,mut list
and x
。模式的其他示例是左侧let lhs = rhs;
和之前的位=>
在 a 的每个分支上match
表达。这些模式如何有效地应用于通话?这真的就像你正在这样做:
fn add(__arg_0: &List, __arg_1: uint) {
let mut list = __arg_0;
let x = __arg_1;
…
}
或许这样看待事物的方式会让事情变得更清晰;函数的签名根本不考虑变量绑定的模式。您的函数签名实际上是规范形式fn add(&List, uint)
. The mut list
部分仅意味着您正在绑定&List
可变名称的值;也就是说,您可以为list
名称,但它在函数之外没有任何作用,这纯粹是变量到位置的绑定问题。
现在讨论第二个问题:了解不可变引用之间的区别(类型&T
, value &x
)和可变引用(类型&mut T
, value &x
)。这些内容非常基础,因此我不会在这里详细介绍它们 - 它们在其他地方已有足够的记录,您可能应该阅读这些内容。我只想说:如果你想改变某些东西,你需要&mut
, not &
,所以你的add
需要采取的方法&mut List
.
第三个问题,所有权问题:在 Rust 中,每个对象都属于正是一个地点;没有垃圾收集或任何东西,这种所有权的唯一性意味着一旦对象超出范围,它就会被销毁。在这种情况下,有问题的表达式是&*(box List::Node(x, box List::End))
。您已将一个值装箱,但实际上并未将其存储在任何地方:您只是尝试引用其中包含的值,但该框将立即被删除。在这种情况下,您真正想要的是修改List
;你想写*list = List::Node(x, box List::End)
,意思是“存储一个List::Node
内容内的值list
“ 代替list = &…
,意思是“分配给变量list
一个新的参考”。
你对价值观的限制也有点过分了。我倾向于这么说new()
应该返回一个List
, not a Box<List>
,尽管这个问题还有待商榷。无论如何,这是add
我最终得到的方法:
fn add(list: &mut List, x: uint) {
match *list {
List::End => *list = List::Node(x, box List::End),
List::Node(_, box ref mut next_node) => add(next_node, x),
}
}
您可能遇到的主要困难是模式box ref mut next_node
. The box ref mut
部分内容为“从盒子中取出值,然后创建对该值的可变引用”;因此,给定一个Box<List>
,它产生一个&mut List
指的是那个盒子里的东西。请记住,与普通表达式相比,模式是完全从后到前的。
最后,我强烈推荐使用impl
对于所有这些,将所有方法放在List
type:
enum List {
Node(uint, Box<List>),
End,
}
impl List {
fn new() -> List {
List::End
}
fn add(&mut self, x: uint) {
match *self {
List::End => *self = List::Node(x, box List::End),
List::Node(_, box ref mut next_node) => next_node.add(x),
}
}
}
fn main() {
let mut list = List::new();
list.add(10);
}