代码之家  ›  专栏  ›  技术社区  ›  Andrew Sun

为什么当mut T工作时,我不能使用一个mut Box多次借用一个可变变量?

  •  2
  • Andrew Sun  · 技术社区  · 6 年前

    我试图在Rust中实现一个链表,但在理解这两个函数之间的差异时遇到了一些困难:

    enum List<T> {
        Nil,
        Cons(T, Box<List<T>>)
    }
    
    fn foo<T>(list: &mut Box<List<T>>) {
        match **list {
            List::Nil => return,
            List::Cons(ref mut head, ref mut tail) => {
                // ...
            }
        }
    }
    
    fn bar<T>(list: &mut List<T>) {
        match *list {
            List::Nil => return,
            List::Cons(ref mut head, ref mut tail) => {
                // ...
            }
        }
    }
    

    foo 未能编译,错误如下:

    error[E0499]: cannot borrow `list` (via `list.1`) as mutable more than once at a time
      --> src/main.rs:66:34
       |
    66 |         List::Cons(ref mut head, ref mut rest) => {
       |                    ------------  ^^^^^^^^^^^^ second mutable borrow occurs here (via `list.1`)
       |                    |
       |                    first mutable borrow occurs here (via `list.0`)
    ...
    69 |     }
       |     - first borrow ends here
    

    然而 bar 编译和运行完美。为什么 酒吧 工作,但不是 foo公司 ?我使用的是Rust版本1.25。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Shepmaster Tim Diekmann    6 年前

    这可以简化为

    fn foo(v: &mut Box<(i32, i32)>) {
        match **v {
            (ref mut head, ref mut tail) => {}
        }
    }
    

    fn foo(v: &mut Box<(i32, i32)>) {
        let (ref mut head, ref mut tail) = **v;
    }
    

    问题是 Box 是一种奇怪的介于两者之间的类型。

    追溯到Rust的历史, 是编译器的特例;它知道很多 ,但这意味着它是“魔法”,没有其他人可以实现像

    RFC 130 提议更改;制作 “只是另一种类型”。不幸的是,这还没有完全转变。

    细节有细微差别,但基本上当前的借用检查器处理模式匹配 句法上 语义上 。它需要这样做,以防止一些不健全的问题。

    在未来,非词汇生存期(NLL)会神奇地解决这个问题;你不必做任何事(万岁!)。

    在此之前,您可以显式返回到 &mut T 带着这个丑陋的斑点:

    match *&mut **list {
    

    或致电 DerefMut 明确:

    match *std::ops::DerefMut::deref_mut(list) {
    

    然而 very little reason to accept a &mut Box<T>

    另请参见: