代码之家  ›  专栏  ›  技术社区  ›  Some Name

将RefCell方法参数分配给局部变量会产生编译错误

  •  4
  • Some Name  · 技术社区  · 1 年前

    考虑以下简单示例:

    use std::cell::RefCell;
    
    // Compiles fine
    fn good(t: RefCell<String>) -> bool {
        t.borrow().len() == 12
    }
    
    // error[E0597]: `t` does not live long enough
    fn bad(t: RefCell<String>) -> bool {
        let t = t;
        t.borrow().len() == 12
    }
    

    Playground

    这个 bad 函数编译失败,出现以下错误:

    error[E0597]: `t` does not live long enough
      --> src/lib.rs:9:5
       |
    8  |     let t = t;
       |         - binding `t` declared here
    9  |     t.borrow().len() == 12
       |     ^---------
       |     |
       |     borrowed value does not live long enough
       |     a temporary with access to the borrow is created here ...
    10 | }
       | -
       | |
       | `t` dropped here while still borrowed
       | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, String>`
       |
       = note: the temporary is part of an expression at the end of a block;
               consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
    help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
       |
    9  |     let x = t.borrow().len() == 12; x
       |     +++++++                       +++
    

    这对我来说非常奇怪。仅仅将函数参数重新分配给局部变量就无法编译。你能解释一下原因吗?

    1 回复  |  直到 1 年前
        1
  •  6
  •   drewtato    1 年前

    中提到了这一点 Reference :

    在函数体的最终表达式中创建的临时变量将被丢弃在绑定在函数体中的任何命名变量之后。它们的放置范围是整个函数,因为没有更小的封闭临时范围。

    我不确定这是否是语言运行所必需的,是否使语言更方便,或者只是编译器最初的实现方式,但现在进行更改是向后不兼容的。如果有人知道更多,请评论或编辑此答案。

    我已经意识到了这一点,有时在理解订单时会出现这种情况。对我来说,新的是,函数自变量显然会被丢弃 甚至更晚 。这几乎被提到了 here 但不完全如此。因此,下订单必须是:

    • 函数内部创建的变量
    • 在最终表达中创建的临时
    • 函数自变量

    这是一个演示这一点的程序。

    struct A(u32);
    
    impl A {
        fn nothing(&self) {}
    }
    
    impl Drop for A {
        fn drop(&mut self) {
            println!("drop {}", self.0);
        }
    }
    
    fn f(_a1: A) {
        let _a3 = A(3);
        A(2).nothing()
    }
    
    fn main() {
        let a = A(1);
        f(a);
    }
    

    它打印以下内容:

    drop 3
    drop 2
    drop 1
    

    在代码中,最后一个表达式创建一个临时 std::cell::Ref<'_, String> 自从 Ref Drop 具体来说,生存期包含在实现的类型中 与普通的共享引用不同,它要求在发生丢弃时生存期有效 & 。由于它借用了 RefCell<String> 这个 裁判 必须在之前删除 RefCell 被丢弃。根据上述规则,当 RefCell 绑定在函数内部。

    错误消息中的解决方法应该始终足够。