rust - 第一个可变借用应该超出范围,但编译器在同一范围内看到两个可变借用
问题描述
这是一个最小的例子:
use crate::List::{Cons, Nil};
enum List {
Cons(i32, Box<List>),
Nil,
}
struct Tail<'a> {
tail: &'a mut List,
}
impl<'a> Tail<'a> {
fn tail(list: &'a mut List) -> Self {
let mut next = list;
loop {
match next {
Cons(_, t) => match t as &List {
Cons(_, _) => next = t,
Nil => break,
},
Nil => break,
}
}
Self { tail: next }
}
}
fn main() {
let mut bar = Cons(20, Box::from(Nil));
let _tail = Tail::tail(&mut bar);
}
我越来越
error[E0499]: cannot borrow `*next` as mutable more than once at a time
--> src/main.rs:24:22
|
12 | impl<'a> Tail<'a> {
| -- lifetime `'a` defined here
...
17 | Cons(_, t) => match t as &List {
| - first mutable borrow occurs here
...
24 | Self { tail: next }
| -------------^^^^--
| | |
| | second mutable borrow occurs here
| returning this value requires that `next.1` is borrowed for `'a`
我误解了一些东西,并认为匹配臂t
中的(第一个可变借用)应该在(第二个可变借用)发生Cons(_, t)
时超出范围。tail: next
也许重要的是要注意,我不认为这项next = t
作业是一个举动,而是像next = t.deref_mut()
. 但是,为什么 Rust 编译器认为我在同一范围内有两个可变借用?有没有办法告诉编译器t
在它的词法范围内的匹配块之后删除?
这个版本在解构为可变借用之前,我尝试在同一迭代中更快地向前看,但是哦,伙计,看起来肯定有更好的方法不是吗?
use crate::List::{Cons, Nil};
enum List {
Cons(i32, Box<List>),
Nil,
}
#[allow(dead_code)]
struct Tail<'a> {
tail: &'a mut List,
}
impl<'a> Tail<'a> {
fn tail(list: &'a mut List) -> Self {
let mut next = list;
loop {
match next {
Cons(_, t) => match t as &List {
Cons(_, _) => {
next = t;
match next {
Nil => return Self { tail: next },
_ => continue,
}
}
_ => panic!(),
},
Nil => return Self { tail: next },
}
}
}
}
fn main() {
let mut bar = Cons(20, Box::from(Nil));
let _tail = Tail::tail(&mut bar);
}
等等,不,编译器实际上让我弄乱了运行时逻辑。好的,重构做一个 O( 2 N) 操作 bc 没有办法一次性做到这一点 AFAIK:
use crate::List::{Cons, Nil};
use std::fmt::Debug;
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
#[derive(Debug)]
struct Tail<'a> {
tail: &'a mut List,
}
impl<'a> Tail<'a> {
fn _get_tail_pos(list: &List) -> usize {
let mut next = list;
let mut idx: usize = 0;
loop {
match next {
Cons(_, t) => {
idx += 1;
next = t;
}
Nil => break,
}
}
idx
}
fn tail(list: &'a mut List) -> Self {
let tail_pos = Self::_get_tail_pos(list);
let mut next = list;
if tail_pos == 0 || tail_pos == 1 {
Self { tail: next }
} else {
let mut i: usize = 0;
loop {
match next {
Cons(_, t) => {
next = t;
i += 1;
if i == tail_pos - 1 {
return Self { tail: next };
}
}
Nil => panic!()
}
}
}
}
}
fn main() {
let mut bar = Nil;
let tail = Tail::tail(&mut bar);
println!("{:?}", tail);
let mut bar = Cons(20, Box::from(Nil));
let tail = Tail::tail(&mut bar);
println!("{:?}", tail);
let mut bar = Cons(20, Box::from(Cons(30, Box::from(Nil))));
let tail = Tail::tail(&mut bar);
println!("{:?}", tail);
}
上述输出:
Tail { tail: Nil }
Tail { tail: Cons(20, Nil) }
Tail { tail: Cons(30, Nil) }
这是预期的行为。TL;DR,我最终首先搜索了索引位置,然后我尝试通过数据结构递归找到我想要放置&mut
. 也许这就是 Rust 编译器希望我设计它的方式。
解决方案
推荐阅读
- azure - 在 cosmosdb 中,我应该使用 id、资源 id 还是自链接来引用其他文档?
- c++ - Visual C++ 在项目中找不到引用的命名空间
- android - NotificationListenerService 方法的工作原理
- c# - StreamReader 将 ReadLine 保存为字符串并在 if 中使用它
- angular - Angular 6 in-memory-api 返回未定义
- javascript - 我制作的脚本不起作用,我找不到问题
- docker - Docker、主机操作系统重启和繁忙的端口
- javascript - 如何在滚动时显示和隐藏 div
- java - Java将跨度添加到图像背景颜色
- sql-server - T-SQL INSTEAD OF UPDATE 触发器:使用 INSERTED 表中的对应列更新所有列