为什么结构中的Box <T>需要"显式生命周期约束"?

Eug*_*sov 21 rust rust-obsolete

编者注:实施RFC 599后,此代码不再产生相同的错误,但答案中讨论的概念仍然有效.

我正在尝试编译此代码:

trait A {
    fn f(&self);
}

struct S {
    a: Box<A>,
}
Run Code Online (Sandbox Code Playgroud)

我收到这个错误:

a.rs:6:13: 6:14 error: explicit lifetime bound required
a.rs:6     a: Box<A>,
Run Code Online (Sandbox Code Playgroud)

我想S.a拥有一个实例A,并且不知道这个生命周期是如何合适的.我需要做些什么才能使编译器满意?

我的Rust版本:

rustc --version
rustc 0.12.0-pre-nightly (79a5448f4 2014-09-13 20:36:02 +0000)
Run Code Online (Sandbox Code Playgroud)

huo*_*uon 21

(稍微迂腐一点:这A是一个特性,所以S不拥有一个实例A,它拥有一个实现的某种类型A的盒装实例.)

特征对象表示具有某种未知类型的数据,也就是说,唯一已知的数据是它实现了特征A.由于类型未知,编译器无法直接推断所包含数据的生命周期,因此需要在特征对象类型中明确说明此信息.

这是通过Trait+'lifetime.最简单的方法是使用'static,即完全禁止存储因范围而变得无效的数据:

a: Box<A + 'static>
Run Code Online (Sandbox Code Playgroud)

以前,(在explicit lifetime bound required引入生命周期特征对象的可能性和引入此错误消息之前)所有盒装特征对象都是隐式的'static,也就是说,这种限制形式是唯一的选择.

最灵活的形式是在外部暴露生命:

struct S<'x> {
    a: Box<A + 'x>
}
Run Code Online (Sandbox Code Playgroud)

这允许S存储实现的任何类型的特征对象A,可能对其S有效的范围有一些限制(即,对于'x小于'static该S对象的类型将被捕获在某个堆栈帧内).


Arj*_*jan 18

这里的问题是也可以为引用实现一个特性,所以如果你没有为Box指定所需的生命周期,那么任何东西都可以存储在那里.

您可以在此rfc中查看有关生命周期的要求.

因此,一种可能的解决方案是绑定生命周期Send(我们把我放在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();
}
Run Code Online (Sandbox Code Playgroud)

另一种是将生命周期设置为'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();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这是更通用的,我们可以存储引用和拥有的数据(Send具有生命周期的类型'static),但是在使用类型的任何地方都需要生命周期参数.