在 Rust 中,当装箱作为泛型参数传递的值时,为什么需要“静态”生命周期界限?

yaw*_*awn 5 lifetime rust box

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

让我困惑的是以下解决方案,添加了一个'static界限:

fn f(arg: impl Trait + 'static) -> Box<dyn Trait> {
    Box::new(arg)
}
Run Code Online (Sandbox Code Playgroud)

现在,它当然可以工作,但原则上我们现在只能在'static生命周期内传递值。x然而,编译器让我毫无问题地通过。

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

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

请注意,1)主要由Why does Rust require a `'static`lifetime for this variable?回答。但我很困惑,这是否是拳击辩论的惯用方式。


编辑:

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

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

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

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

Col*_*Two 4

您在这里有一些误解。

x 不是有生命周期,驻留在堆栈上吗?当 arg 具有“静态生命周期限制”时,为什么可以将其传递给 f?这些界限只涉及引用的生命周期吗?

当您执行 时f(x),因为您没有引用x,所以您正在的值x移至函数中,这会改变其生命周期。x如果您在调用后尝试再次使用f(x),Rust 将无法编译您的代码并告诉您这一点。

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

fn f<'a>(arg: impl Trait + 'a) -> Box<dyn Trait + 'a> {
    Box::new(arg)
}
Run Code Online (Sandbox Code Playgroud)