在 Rust 中,当装箱作为通用参数传递的值时,为什么需要“静态”生命周期绑定?

In Rust, when boxing a value passed as a generic argument, why is a `'static` lifetime bound required?

我正在尝试编写一个构造函数,该函数采用通过参数实现某些特征的通用值,然后将其装箱(重点是然后用这些箱子初始化一些东西,但以下示例已简化):

struct Struct {}

trait Trait {}

impl Trait for Struct {}

fn f(arg: impl Trait) -> Box<dyn Trait> {
    Box::new(arg)
}

fn main() {
    let x = Struct {};
    f(x);
}

在这里,编译器抱怨说 arg 可能活得不够长。这对我来说很有意义,因为对 arg 的唯一要求是 impl Trait,这意味着它实际上可能是一个实现特征的引用,在这种情况下它不能被安全地装箱。

让我感到困惑的是以下解决方案,添加了一个 'static 绑定:

fn f(arg: impl Trait + 'static) -> Box<dyn Trait> {
    Box::new(arg)
}

现在,它当然可以工作,但原则上我们现在必须传递具有 'static 生命周期的值。然而,编译器让我毫无问题地传入 x

这是我的问题,更具体地说:

  1. 难道x没有生命周期,驻留在栈上吗?当 arg 具有 'static 生命周期限制时,为什么可以将它传递给 f?这些边界只涉及引用的生命周期吗?
  2. 这个解决方案通常有效吗,或者我会遇到编译器拒绝我的堆栈分配参数的情况吗?
  3. 有没有更好的方式来表达“任何暗示 Trait 类型不是引用的类型?

注意 1) 大部分由 回答,但我很困惑这是否是拳击论证的惯用方式。


编辑:

现在我理解得更好了,我想知道在填充结构的情况下修复编译器错误的惯用解决方案是什么:

struct OtherStruct {
    x: Box<dyn Trait>,
}

impl OtherStruct {
    fn new(arg: impl Trait) -> Self { // Does not compile
        Self { x: Box::new(arg) }
    }
}

到目前为止我看到的唯一解决方案是 1) 向 OtherStruct 添加一个生命周期参数(不太好),向 arg 添加一个 'static 生命周期绑定(我是不确定这样可以吗?)

你有几个误解。

Doesn't x have a lifetime, residing on the stack? Why is it possible to pass it to f when arg has a 'static lifetime bound? Do these bounds only concern the lifetime of references?

当你做 f(x) 时,因为你没有引用 x,你正在 移动 [=12= 的值] 进入函数,它改变了它的生命周期。如果你在调用 f(x) 之后再次尝试使用 x,Rust 将无法编译你的代码并告诉你这个。

至于 + 'static 绑定... Box<dyn Trait> 是 shorthand 对应 Box<dyn Trait + 'static>,这就是编译器给您错误的原因。类型系统需要知道盒子内部实现的生命周期,以便它可以检查它。如果你愿意,你可以给盒子一个不同的生命周期,明确地:

fn f<'a>(arg: impl Trait + 'a) -> Box<dyn Trait + 'a> {
    Box::new(arg)
}