Rust 迭代递归结构时无法获取可变引用:一次不能多次借用可变引用
我试图迭代地导航递归数据结构,以便在特定位置插入元素。就我有限的理解而言,这意味着对结构的根进行可变引用,然后依次替换为对其跟随者的引用:Rust 迭代递归结构时无法获取可变引用:一次不能多次借用可变引用,rust,mutable,borrowing,Rust,Mutable,Borrowing,我试图迭代地导航递归数据结构,以便在特定位置插入元素。就我有限的理解而言,这意味着对结构的根进行可变引用,然后依次替换为对其跟随者的引用: type Link = Option<Box<Node>>; struct Node { next: Link } struct Recursive { root: Link } impl Recursive { fn back(&mut self) -> &mut Link {
type Link = Option<Box<Node>>;
struct Node {
next: Link
}
struct Recursive {
root: Link
}
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while let Some(ref mut node) = *anchor {
anchor = &mut node.next;
}
anchor
}
}
这是有意义的,因为anchor
和node
都引用了相同的结构,但我实际上不再关心anchor
在对其进行解构之后
如何使用safe Rust正确实现
back()
?您可以使用递归来满足借用检查器的要求。这样做的缺点是为列表中的每个项目创建堆栈框架。如果列表很长,这肯定会导致堆栈溢出。LLVM将把节点::back
方法优化为一个循环(请参阅在上生成的LLVM IR)
impl节点{
fn返回(&mut self)->&mut链接{
匹配self.next{
Some(ref mut node)=>node.back(),
无=>&mut self.next,
}
}
}
简单递归{
fn返回(&mut self)->选项{
self.root.as_mut().map(| node | node.back())
}
}
有可能。。。但我希望我有一个更优雅的解决方案
诀窍是不要借用锚点
,因此要在两个蓄能器之间切换:
- 持有对当前节点的引用的节点
- 另一个节点被分配到下一个节点的引用
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
loop {
let tmp = anchor;
if let Some(ref mut node) = *tmp {
anchor = &mut node.next;
} else {
anchor = tmp;
break;
}
}
anchor
}
}
不太漂亮,但这是借书人可以做到的\_(ツ)_/''
@ker通过创建一个未命名的临时文件对此进行了改进:
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
loop {
match {anchor} {
&mut Some(ref mut node) => anchor = &mut node.next,
other => return other,
}
}
}
}
这里的技巧是使用{anchor}
将锚定
的内容移动到一个未命名的临时文件中,匹配将在该临时文件上执行。因此,在匹配
块中,我们不是从锚定
中借用,而是从临时文件中借用,让我们可以自由修改锚定
。请参阅相关的博客文章。它起作用:
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while anchor.is_some(){
anchor = &mut {anchor}.as_mut().unwrap().next;
}
anchor
}
一旦启用,原始代码将按原样工作:
type Link = Option<Box<Node>>;
struct Node {
next: Link,
}
struct Recursive {
root: Link,
}
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while let Some(node) = anchor {
anchor = &mut node.next;
}
anchor
}
}
类型链接=选项;
结构节点{
下一篇:链接,
}
结构递归{
根:链接,
}
简单递归{
fn返回(&mut self)->&mut链接{
让mut锚定=&mut self.root;
而让一些(节点)=锚定{
锚点=&mut node.next;
}
锚
}
}
非词汇生存期提高了编译器借用检查器的精度,允许它看到不再使用
锚定的可变借用。由于最近的语言更改,我们还可以简化if let
中的关键字。太棒了!让我了解这里发生了什么:1)anchor
具有初始引用2)tmp
从anchor
移动,这意味着anchor
不再是引用3)tmp
可以安全地从中借用,因为它在循环迭代结束时就被丢弃了。这里最可怕的是,我最初忘记了anchor=tmp在else
分支中的code>,rustc为此引发了一个错误。。。无论如何,是的,这个想法是在借用anchor
时不能重新分配它,因此我们将引用转移到tmp
,然后借用tmp
分配anchor
。这实际上可以写得非常简洁,因为我们可以在移动anchor
之前调用is_some()
。我已经编辑了你的帖子。这里有一个没有临时或展开的解决方案版本:@FabianKnorr:我不喜欢在我可以避免的地方使用unwrap
,因为虽然它是安全的,但它也是(潜在)崩溃的根源。
type Link = Option<Box<Node>>;
struct Node {
next: Link,
}
struct Recursive {
root: Link,
}
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while let Some(node) = anchor {
anchor = &mut node.next;
}
anchor
}
}