为什么借用检查器会拒绝内联表达式但接受其分解形式?

4

我天真地期望下面的两个函数 try_drain_* 有相同的行为,然而第一个无法编译,而第二个则可以顺利执行。

struct Container {
    map: RefCell<HashMap<i32, i32>>,
}

impl Container {
    fn try_drain_inline(&self) {
        self.map.borrow_mut().drain();
    }

    fn try_drain_broken_down(&self) {
        let mut guard = self.map.borrow_mut();
        guard.drain();
    }
}

借用检查器对 try_drain_inline 在 playground 上 提出了投诉:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:15:5
   |
14 |         self.map.borrow_mut().drain();
   |         ---------------------        ^ temporary value dropped here while still borrowed
   |         |
   |         temporary value created here
   |
   = note: values in a scope are dropped in the opposite order they are created

对于try_drain_broken_down而言,这是可以的。

看起来问题出在创建的临时变量销毁的顺序上;“手动”实例化这些临时变量可以解决这个问题……

为什么借用检查器会拒绝内联形式而接受分解形式呢?


注意:我的真实代码是一个需要两个中间变量的try_pop函数:

fn try_pop(&self) -> Option<i32> {
    let mut guard = self.map.borrow_mut();
    let mut drain = guard.drain();
    drain.next().map(|(_, t)| t)
}

1
可能相关:https://dev59.com/1VcP5IYBdhLWcg3w8uZi; https://github.com/rust-lang/rust/issues/21114 - Matthieu M.
也许这与此相关:http://smallcultfollowing.com/babysteps/blog/2017/07/11/non-lexical-lifetimes-draft-rfc-and-prototype-available/ - Boiethios
1个回答

2

因为内联版本等同于以下代码:

fn try_drain_inline_broken_down(&self) {
    {   // Implicit block around the whole expression
        let mut guard = self.map.borrow_mut();
        guard.drain()    // No semicolon here
    };                   // The semicolon is here
}

一旦按照这种方式编写,您会注意到drain返回的值需要足够长时间存活以从块中返回,但它保留了对guard的引用,而guard仅在块结束时才存在。

在您分解的版本中,drain返回的值仅在分号之前存在,而guard则存在于块的末尾,因此一切都很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接