传递给函数的函数的引用仍然是借用的。

4
考虑以下 Rust 代码:
fn foo<'a, T, F, G>(x: &'a mut T, f: F, g: G)
where
    T: 'a,
    F: Fn(&'a T) -> &'a T,
    G: Fn(&'a mut T) -> &'a mut T,
{
    {
        f(x);
    }
    g(x);
}

fn main() {
    let mut x = 5;
    foo(&mut x, |a| a, |a| a);
}

这会导致编译器错误:

error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
  --> src/main.rs:10:7
   |
8  |         f(x);
   |           - immutable borrow occurs here
9  |     }
10 |     g(x);
   |       ^ mutable borrow occurs here
11 | }
   | - immutable borrow ends here

我不明白为什么在第11行不可变的借用x会结束。首先,f(x)在一个内部范围内,该范围在第9行结束。但是,f(x)的返回值没有绑定到任何变量,因此我认为借用应该在第8行结束,而内部范围甚至不必要。


@Shepmaster 注意,这个问题是关于返回相同生命周期的。当函数不返回任何内容时,这里的问题同样失败了。链接 - mcarton
@mcarton 我仍在寻找使用方法而非函数的真正重复项。闭包和方法都有相同的根本问题,即它们可以在自身内部存储引用,正如你所指出的那样。 - Shepmaster
1个回答

4

让我们考虑这个例子

fn foo<'a, T, F, G>(x: &'a mut T, mut f: F, g: G)
where
    T: 'a,
    F: FnMut(&'a T) -> &'a T,
    G: Fn(&'a mut T) -> &'a mut T,
{
    {
        f(x);
    }
}

fn main() {
    let mut x = 5;
    let mut y = std::cell::RefCell::new(&0);
    foo(&mut x, |a| { y.replace(&a); a }, |a| a);
}

这是完全合法的,因为函数f保证会接受与x生命周期相同的引用,所以它可以存储对x的引用。但是,您无法使用x调用g,因为f可能已经存储了x
如果您将foo更改为:
fn foo<T, F, G>(x: &mut T, mut f: F, g: G)
where
    F: FnMut(&T) -> &T,
    G: Fn(&T) -> &T,

由于生命周期省略规则,这等同于:

fn foo<'a, T, F, G>(x: &'a mut T, mut f: F, g: G)
where
    T: 'a,
    F: for<'b> FnMut(&'b T) -> &'b T,
    G: for<'c> Fn(&'c T) -> &'c T,

然后 f 不允许存储引用x:

error: borrowed data cannot be stored outside of its closure
  --> src/main.rs:14:33
   |
13 |     let mut y = std::cell::RefCell::new(&0);
   |         -----                           -- cannot infer an appropriate lifetime...
   |         |
   |         ...so that variable is valid at time of its declaration
14 |     foo(&mut x, |a| { y.replace(&a); a }, |a| a);
   |                 ---             ^^ cannot be stored outside of its closure
   |                 |
   |                 borrowed data cannot outlive this closure

但是调用foo作为foo(&mut x, |a| a, |a| a); 变得合法


@Shepmaster,我真的在想,是不是应该有一个rustfmt机器人来编辑Rust标签下的每一个问题和答案。你什么时候睡觉啊? :D - mcarton
我的意思是...我基本上创建了我的游乐场实现,唯一的目的是能够运行rustfmt。在这个问答中,情况特别严重,通用声明看起来非常接近Perl级别的行噪声。 - Shepmaster
由于生命周期省略规则,这实际上是生命周期省略吗?我认为 HRTB 不是省略的一部分。我猜它们有点必须是吗? - Shepmaster
1
什么使您省略生命周期,但又符合生命周期省略规则呢? - mcarton

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