首页 > 解决方案 > 第一个可变借用应该超出范围,但编译器在同一范围内看到两个可变借用

问题描述

这是一个最小的例子:

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 编译器希望我设计它的方式。

标签: rustreferenceborrow-checker

解决方案


推荐阅读