Reference 为什么我不能从闭包返回外部变量的可变引用?

Reference 为什么我不能从闭包返回外部变量的可变引用?,reference,rust,closures,lifetime,mutable,Reference,Rust,Closures,Lifetime,Mutable,当我遇到这个有趣的场景时,我正在玩生锈的闭包: fn main() { let mut y = 10; let f = || &mut y; f(); } 这会产生一个错误: 错误[E0495]:由于需求冲突,无法推断借用表达式的适当生存期 -->src/main.rs:4:16 | 4 |设f=| |&mut y; | ^^^^^^ | 注意:首先,寿命不能超过身体上定义的4:13的寿命。。。 -->src/main.rs:4

当我遇到这个有趣的场景时,我正在玩生锈的闭包:

fn main() {
    let mut y = 10;

    let f = || &mut y;

    f();
}
这会产生一个错误:

错误[E0495]:由于需求冲突,无法推断借用表达式的适当生存期
-->src/main.rs:4:16
|
4 |设f=| |&mut y;
|                ^^^^^^
|
注意:首先,寿命不能超过身体上定义的4:13的寿命。。。
-->src/main.rs:4:13
|
4 |设f=| |&mut y;
|             ^^^^^^^^^
注意:…以便闭包可以访问'y'`
-->src/main.rs:4:16
|
4 |设f=| |&mut y;
|                ^^^^^^
注意:但是,生命周期必须对6:5的呼叫有效。。。
-->src/main.rs:6:5
|
6 | f();
|     ^^^
注意:…因此表达式的类型“%mut i32”在表达式运行期间有效
-->src/main.rs:6:5
|
6 | f();
|     ^^^
即使编译器试图逐行解释它,我仍然不明白它到底在抱怨什么

它是在试图说可变引用不能在封闭闭包之外生存吗

如果我删除调用
f()

fn main() {
    let mut y: u32 = 10;

    let ry = &mut y;
    let f = || ry;

    f();
}
它之所以有效,是因为编译器能够推断
ry
的生存期:引用
ry
y
的相同范围内

现在,代码的等效版本为:

fn main() {
    let mut y: u32 = 10;

    let f = || {
        let ry = &mut y;
        ry
    };

    f();
}
现在,编译器为
ry
分配与闭包主体作用域相关联的生存期,而不是与主体相关联的生存期

还要注意的是,不可变参考案例是有效的:

fn main() {
    let mut y: u32 = 10;

    let f = || {
        let ry = &y;
        ry
    };

    f();
}
这是因为
&T
具有复制语义,而
&mut T
具有移动语义,有关详细信息,请参阅

丢失的那一块 编译器抛出与生存期相关的错误:

cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
但正如斯文·马纳奇所指出的,也有一个与错误相关的问题

cannot move out of borrowed content
但是为什么编译器不抛出这个错误呢

简而言之,编译器首先执行类型检查,然后执行借用检查

长话短说 封口由两部分组成:

  • 闭包的状态:包含闭包捕获的所有变量的结构

  • 闭包的逻辑:实现
    FnOnce
    FnMut
    Fn
    特征

在这种情况下,闭包的状态是可变引用
y
,逻辑是只返回可变引用的闭包体
{&mut y}

遇到参考时,锈蚀控制两个方面:

  • 状态:如果引用指向有效内存片(即生存期有效性的只读部分)

  • 逻辑:如果内存片有别名,换句话说,如果它同时指向多个引用

  • 注意:为了避免内存混叠,禁止从借用内容移出

    Rust编译器通过以下简化的工作流执行其工作:

    .rs input -> AST -> HIR -> HIR postprocessing -> MIR -> HIR postprocessing -> LLVM IR -> binary
    

    编译器报告生存期问题,因为它首先在
    HIR postprocessing
    (包括生存期分析)中执行类型检查阶段,然后,如果成功,在
    MIR postprocessing
    阶段执行借用检查。

    这里有两个主要问题:

  • 闭包不能返回对其环境的引用
  • 对可变引用的可变引用只能使用外部引用的生存期(与不可变引用不同)

  • 返回对环境的引用的闭包 闭包不能返回生存期为
    self
    (闭包对象)的任何引用。为什么呢?每个闭包都可以称为
    FnOnce
    ,因为这是
    FnMut
    的超级特性,而这又是
    Fn
    的超级特性
    FnOnce
    有以下方法:

    fn call_once(self, args: Args) -> Self::Output;
    
    请注意,
    self
    是通过值传递的。因此,由于
    self
    已被使用(现在位于
    call\u once
    函数中),我们无法返回对它的引用——这相当于返回对局部函数变量的引用

    理论上,
    调用mut
    将允许返回对
    self
    的引用(因为它接收
    和mut self
    )。但是由于
    call\u once
    call\u mut
    call
    都是用同一个主体实现的,所以闭包通常不能返回对
    self
    的引用(即:对其捕获的环境的引用)

    可以肯定的是:闭包可以捕获引用并返回这些引用!他们可以通过引用捕获并返回引用。这些东西是不同的。它只是关于闭包类型中存储的内容。如果类型中存储有引用,则可以返回该引用。但是我们不能返回对存储在闭包类型中的任何内容的引用

    嵌套可变引用 考虑此函数(请注意,参数类型意味着
    'inner:'outer
    'outer
    'inner
    短):

    返回的引用应该具有什么生存期

    • 它不可能是
      'a
      ,因为我们基本上有一个
      &的mut&'a mut i32
      。如上所述,在这种嵌套可变引用情况下,我们无法提取更长的生命周期
    • 但它也不能是
      ,因为这意味着闭包返回的东西的生命周期为
      'self
      (“借用自
      self
      ”)。如上所述,闭包不能做到这一点
    所以编译器不能为我们生成闭包impls

    短版 闭包
    f
    存储对
    y
    的可变引用。如果允许它返回此引用的副本,您将得到两个simult
    fn foo<'outer, 'inner>(x: &'outer mut &'inner mut i32) -> &'inner mut i32 {
        *x
    }
    
    let mut y = 10;
    
    struct Foo<'a>(&'a mut i32);
    impl<'a> Foo<'a> {
        fn call<'s>(&'s mut self) -> &'??? mut i32 { self.0 }
    }
    
    let mut f = Foo(&mut y);
    f.call();
    
    struct __Closure<'a> {
        y: &'a mut i32,
    }
    
    fn call_mut(&mut self, args: ()) -> &'a mut i32 { self.y }
    
    fn main() {
        let x = String::new();
        let mut y: u32 = 10;
        let f = || {
            drop(x);
            &mut y
        };
        f();
    }
    
    fn make_fn_once<'a, T, F: FnOnce() -> T>(f: F) -> F {
        f
    }
    
    fn main() {
        let mut y: u32 = 10;
        let f = make_fn_once(|| {
            &mut y
        });
        f();
    }