为什么 Rust 借用检查器不能利用“静态”边界?

Why can't the Rust borrow checker take advantage of `'static` bounds?

考虑 following Rust program:

#![feature(generic_associated_types)]

pub trait Func {
    type Input<'a>;
    type Output;
    
    fn call(self, input: Self::Input<'_>) -> Self::Output;
}

fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
    where F: 'static + Func<Input<'cx> = &'cx u8, Output = u8>,
{
    let input = &*ctx;
    let out = f.call(input);
    *ctx = out;
}

我用过 #![feature(generic_associated_types)],但我认为如果您将 'aFunc::Input 移动到 Func 并使用invoke.

上的高阶特征绑定

这段代码有误,但我认为它没有问题:

error[E0506]: cannot assign to `*ctx` because it is borrowed
  --> src/lib.rs:15:5
   |
10 | fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
   |         --- lifetime `'cx` defined here
...
13 |     let input = &*ctx;
   |                 ----- borrow of `*ctx` occurs here
14 |     let out = f.call(input);
   |               ------------- argument requires that `*ctx` is borrowed for `'cx`
15 |     *ctx = out;
   |     ^^^^^^^^^^ assignment to borrowed `*ctx` occurs here

首先 ctx 被重新借用为 input,它被传递给 f.call 然后再也没有使用过。 f.call returns一个不包含任何生命周期的值(u8: 'static),所以outctx之间没有联系。

同样,f 的类型不包含生命周期 (F: 'static),因此它不能包含生命周期为 'cx 的引用。此外,无法将生命周期 'cx 安全地强制转换为 call 内的 'static,因此无法“走私”具有该生命周期的引用,该引用可以在调用 [=19= 之外访问].因此,我看不出有什么可以别名 ctx,我认为在最后一行分配给它应该是合理的。

我错过了什么吗?接受这个代码是不合理的吗?如果不是,为什么 Rust 无法以这种方式利用 'static 边界?

生命周期 'cx 可能是 'static 意味着 input 可以 被走私到别处并被 *ctx = out 无效。

无法限制生命周期严格小于另一个生命周期,因此我认为借用检查器甚至不会考虑向泛型类型添加“更广泛”的生命周期约束。

写的代码不合理。 F'static 的生命周期边界完全无关紧要,因为 F::InputF 的生命周期边界是两个不同的生命周期,导致错误的是关联类型的生命周期.通过声明 F::Input<'ctx> = &'ctx u8,您声明不可变借用的寿命与可变引用的长度相同,从而使可变引用使用不安全。

正如@Stargateur 所提到的,可以使这项工作起作用的是更高等级的特征范围:

fn invoke<F>(f: F, ctx: &mut u8)
    where F: for<'ctx> Func<Input<'ctx> = &'ctx u8, Output = u8>,
{
    let input = ctx;
    let out = f.call(input);
    *input = out;
}

Playground

也就是说,不是声明函数调用在某个特定生命周期 'ctx 内有效,而是在所有生命周期 'ctx 内都有效。这样,编译器可以自由地为重新借用选择合适的生命周期来完成这项工作。

附带说明一下,您可能认为在函数定义中使用两个特定的生命周期是可行的,但任何这样做的尝试都会导致编译器 failing to choose 使事情正常进行的适当生命周期.