为什么在结构体中使用Box<T>时需要“显式生命周期绑定”?

23

编辑注:在实现RFC 599之后,此代码不再产生相同的错误,但回答中讨论的概念仍然有效。

我正在尝试编译这段代码:

trait A {
    fn f(&self);
}

struct S {
    a: Box<A>,
}

我遇到了这个错误:

a.rs:6:13: 6:14 error: explicit lifetime bound required
a.rs:6     a: Box<A>,

我希望 S.a 拥有一个 A 的实例,但不知道这里的生命周期是否合适。我需要做什么才能让编译器满意?

我的 Rust 版本:

rustc --version
rustc 0.12.0-pre-nightly (79a5448f4 2014-09-13 20:36:02 +0000)
2个回答

26
略微吹毛求疵的一点是: A 是一个 trait,因此S并没有拥有A的实例,它只拥有一个实现了A的某种类型的封装实例
trait object 代表具有未知类型的数据,也就是说,关于数据唯一已知的事情就是它实现了 traitA。由于类型不知道,编译器无法直接推断所包含数据的生命周期,因此需要在 trait object 类型中明确指定这些信息。
这可以通过 Trait+'lifetime 来完成。 最简单的方法是只使用 'static,也就是完全禁止存储由于作用域而变得无效的数据:
a: Box<A + 'static>

在引入有生命周期限制的特质对象和这个“需要显式生命周期绑定”的错误信息之前,所有的装箱特质对象都是隐式的 'static,也就是说,这种受限制的形式是唯一的选择。

最灵活的形式是将生命周期外部暴露:

struct S<'x> {
    a: Box<A + 'x>
}

这使得S能够存储任何实现A的类型的特质对象,可能对S有效的范围有一些限制(例如,对于'static小于'x的类型,S对象将被限制在某些堆栈帧中)。


22
这里的问题是,特征也可以针对引用实现,因此如果您没有为Box指定所需的生命周期,那么任何内容都可以存储在其中。
您可以在此rfc中查看有关生命周期要求的信息。
因此,一种可能的解决方案是将生命周期绑定到Send(我们将I放入S中):
trait A {
    fn f(&self);
}

struct I;

impl A for I {
    fn f(&self) {
        println!("A for I")
    }
}

struct S {
    a: Box<A + Send>
}

fn main() {
    let s = S {
        a: box I
    };
    s.a.f();
}

另一种方法是将生命周期设置为'a(我们可以将引用&I或I放入S中):
trait A {
    fn f(&self);
}

struct I;

impl A for I {
    fn f(&self) {
        println!("A for I")
    }
}

impl <'a> A for &'a I {
    fn f(&self) {
        println!("A for &I")
    }
}

struct S<'a> {
    a: Box<A + 'a>
}

fn main() {
    let s = S {
        a: box &I
    };
    s.a.f();
}

请注意,这是更为通用的方式,我们可以存储引用和拥有的数据(具有 'static 生命周期的 Send 类型),但您需要在每个使用该类型的地方都要求一个生命周期参数。

感谢提供 RFC 链接,它确实阐明了 Boxes 内部生命周期的一些问题。 - Eugene Zemtsov

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