我正在尝试在Rust中实现一个cons列表作为练习.我设法解决了除了这个之外的所有编译器错误:
Compiling list v0.0.1 (file:///home/nate/git/rust/list)
/home/nate/git/rust/list/src/main.rs:18:24: 18:60 error: borrowed value does not live long enough
/home/nate/git/rust/list/src/main.rs:18 List::End => list = &*(box List::Node(x, box List::End)),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nate/git/rust/list/src/main.rs:16:34: 21:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 16:33...
/home/nate/git/rust/list/src/main.rs:16 fn add(mut list: &List, x: uint) {
/home/nate/git/rust/list/src/main.rs:17 match *list {
/home/nate/git/rust/list/src/main.rs:18 List::End => list = &*(box List::Node(x, box List::End)),
/home/nate/git/rust/list/src/main.rs:19 List::Node(_, ref next_node) => add(&**next_node, x),
/home/nate/git/rust/list/src/main.rs:20 }
/home/nate/git/rust/list/src/main.rs:21 }
/home/nate/git/rust/list/src/main.rs:18:16: 18:60 note: ...but borrowed value is only valid for the expression at 18:15
/home/nate/git/rust/list/src/main.rs:18 List::End => list = &*(box List::Node(x, box List::End)),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `list`.
To learn more, run the command again with --verbose.
Run Code Online (Sandbox Code Playgroud)
我正在尝试编译的代码:
enum List {
Node(uint, Box<List>),
End,
}
fn main() {
let mut list = new();
add(&*list, 10);
//add(list, 20);
//add(list, 30);
print(&*list);
}
fn add(mut list: &List, x: uint) {
match *list {
List::End => list = &*(box List::Node(x, box List::End)),
List::Node(_, ref next_node) => add(&**next_node, x),
}
}
fn new() -> Box<List> {
box List::End
}
Run Code Online (Sandbox Code Playgroud)
那么盒装价值观为何不能活得足够长呢?是因为我立即取消引用它们吗?我这样试过:
match *list {
List::End => {
let end = box List::Node(x, box List::End);
list = &*end;
}
List::Node(_, ref next_node) => add(&**next_node, x),
}
Run Code Online (Sandbox Code Playgroud)
但我得到了完全相同的错误.我错过了什么?
我想你错过了Rust的一些关键细节; 我认为我们需要处理三件事:
&)和mutable(&mut)引用之间的区别;&*box尝试).我先处理模式部分; 在fn add(mut list: &List, x: uint),有两种模式被使用,mut list和x.模式的其他示例是表达式的每个分支的左侧let lhs = rhs;和位之前的位.这些模式如何有效地应用于呼叫?这真的就像你这样做:=>match
fn add(__arg_0: &List, __arg_1: uint) {
let mut list = __arg_0;
let x = __arg_1;
…
}
Run Code Online (Sandbox Code Playgroud)
也许这种看待事物的方式会让它更清晰; 函数的签名根本不考虑变量绑定的模式.您的功能签名实际上是规范形式fn add(&List, uint).该mut list部分仅表示您将&List值绑定到可变名称; 也就是说,您可以为list名称指定一个新值,但它在函数外部没有任何影响,它纯粹是一个变量与位置绑定的问题.
现在进入第二个问题:学习不可变引用(类型&T,值&x)和可变引用(类型&mut T,值&x)之间的区别.这些是非常基础的,我不会在这里详细介绍 - 他们在其他地方充分记录,你应该阅读这些东西.我只想说:如果你想改变某些东西,你需要&mut,而不是&,所以你的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()应该返回a List,而不是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),
}
}
Run Code Online (Sandbox Code Playgroud)
你可能遇到困难的主要部分是模式box ref mut next_node.该box ref mut部分读取"从其框中取出值,然后创建对该值的可变引用"; 因此,给定a Box<List>,它产生一个&mut List引用该框的内容.请记住,与普通表达式相比,模式完全回到前面.
最后,我强烈建议impl在所有这些中使用s,将所有方法放在List类型上:
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);
}
Run Code Online (Sandbox Code Playgroud)
小智 5
不幸的是,您尝试修复其他编译器错误导致了一个不一致的黑暗处.首先,您需要决定是否需要a Box<List>或a List作为(子)列表头部的句柄.让我们一起来,List因为它更灵活,通常是阻力最小的路径.
然后,你需要意识到mut list: &List和之间存在差异list: &mut List.第一个是只读参考,您可以将其更改为指向其他只读参考.第二个是读写引用,您无法将其更改为指向其他读写内容.还有,mut list: &mut List因为这两个功能是正交的.
在add你写的时候list = ...,你只会影响你的局部变量.它对调用者没有影响.您想要更改调用者看到的列表节点!既然我们说要处理List,而不是盒子,我们将改变存在的列表节点的内容(用Enda 替换final Node(..., box End)).也就是说,签名和代码更改如下:
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),
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,*list =不同之处list =在于,我们现在就地更改列表节点的内容,而不是将我们的本地参考点放在不同的节点上.
为了保持一致性和人体工程学(以及一点点效率),您应该更改new为返回裸露List,即:
fn new() -> List {
List::End
}
Run Code Online (Sandbox Code Playgroud)
这也可以&*在调用中为您节省所有令人讨厌的重新借用():
let list = new(); // type: List
add(&mut list, 10);
Run Code Online (Sandbox Code Playgroud)
最后,至于为什么盒子没有足够长的时间:好吧,你基本上创建了一个本地/临时盒子,引用它,然后尝试传递引用而不保持盒子活着.没有所有者的框被取消分配,因此您需要为其提供所有者.在上面的固定代码中,所有者是我们创建和写入的next字段.List::Node&mut List