如何为具有生命周期的结构实现“具有静态生命周期的特征”?

时间:2019-03-14 07:25:57

标签: rust traits lifetime

我有一个要为trait Surface: 'static实现的struct Obj<'a>。特质必须为'static,因为我想将Surface类型的对象存储在Vec<Box<Surface>>中。

第一步,我尝试了这个。

impl<'a> Surface for Obj<'a> {}

由于'static'a之间的生命周期不匹配,因此无法使用。换句话说:SurfaceObj的寿命更长,因为Surface'static。 我对实现进行了如下更改。

impl<'a> Surface for Obj<'a> where 'a: 'static {}

据我对文档的正确理解,我正在做的是'a的寿命可能超过'static。我要这个吗?

如果我转让了Obj<'a>的所有权,编译器会告诉我Obj内部的可变引用寿命不长,并且仍然可以借用。

这是一个简短的例子。

trait Surface: 'static {}

struct Manager {
    storage: Vec<Box<Surface>>,
}

impl Manager {
    fn add(&mut self, surface: impl Surface) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj<'a> {
    data: &'a mut SomeOtherStruct,
}

impl<'a> Obj<'a> {
    fn new(some_struct: &'a mut SomeOtherStruct) -> Self {
        Obj { data: some_struct }
    }
}

impl<'a> Surface for Obj<'a> where 'a: 'static {}

fn main() {
    let mut some_struct = SomeOtherStruct {};
    let mut manager = Manager {
        storage: Vec::new(),
    };

    let obj = Obj::new(&mut some_struct);
    manager.add(obj);
}

Playground

error[E0597]: `some_struct` does not live long enough
  --> src/main.rs:33:24
   |
33 |     let obj = Obj::new(&mut some_struct);
   |               ---------^^^^^^^^^^^^^^^^-
   |               |        |
   |               |        borrowed value does not live long enough
   |               argument requires that `some_struct` is borrowed for `'static`
34 |     manager.add(obj);
35 | }
   | - `some_struct` dropped here while still borrowed

换句话说,&mut some_struct是生命周期'a,但需要'static。好吧,很明显,因为some_struct生活在Obj<'a>中,所以它不可能是'static吗?

这是我要做的“ Rust like”吗?我不知道如何使它工作。它的确与生命周期混淆。我想我可以使用Rc<T>来解决这个问题,但这会使事情变得更复杂。

3 个答案:

答案 0 :(得分:3)

第一件事:

impl<'a> Surface for Obj<'a> where 'a: 'static {}

冗长

impl Surface for Obj<'static> {}

您正确识别了问题:

  

换句话说,&mut some_struct是生命周期'a,但需要'static

您需要将some_struct声明为static

fn main() {
    static mut SOME_STRUCT: SomeOtherStruct = SomeOtherStruct {};
    // ...
    let obj = unsafe { Obj::new(&mut SOME_STRUCT) };
    //  ...
}

问题是,您不能安全地访问可变静态变量,因为它们可以同时在多线程中进行变异,这是一个问题,因此您需要unsafe

所以不,您的代码不是“ Rust like”,但恐怕您无法用当前的体系结构进行更改。


  

特质必须是“静态的,因为我想将Surface类型的对象存储在Vec<Box<Surface>>中。

我不明白为什么您首先认为需要'static,例如该代码完全合法:

trait Foo {}
struct Bar;

impl Foo for Bar {}

fn main() {
    let b: Box<Foo> = Box::new(Bar);
}

答案 1 :(得分:2)

  

如何为寿命为'static的结构实现寿命为'a的特征?

您没有,也没有。 'static生命周期的目的是说“在程序的整个过程中都存在的东西”。除了{em> 'a本身之外,没有任意生存期'static满足此要求。

答案 2 :(得分:1)

@hellow's answer可以解决我的问题,但是对Rust来说,这很hacky。

在您的提示下,我找到了一种更好的解决方案,该解决方案也可以使用并且不使用unsafe

解决方案1 ​​

我为Manager和类型Box<Surface + 'a>指定了明确的生存期参数:

trait Surface {}

struct Manager<'a> {
    storage: Vec<Box<Surface + 'a>>,
}

impl<'a> Manager<'a> {
    fn add(&mut self, surface: impl Surface + 'a) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj<'a> {
    data: &'a mut SomeOtherStruct,
}

impl<'a> Obj<'a> {
    fn new(some_struct: &'a mut SomeOtherStruct) -> Self {
        Obj {
            data: some_struct
        }
    }
}

impl<'a> Surface for Obj<'a> {}

fn main() {
    let mut some_struct = SomeOtherStruct{};
    let mut manager = Manager { storage: Vec::new() };

    let obj = Obj::new(&mut some_struct);
    manager.add(obj);
}

Playground

解决方案2

Box<SomeOtherStruct>中存储&mut SomeOtherStruct而不是Obj。这将消除生命周期:

trait Surface {}

struct Manager {
    storage: Vec<Box<Surface>>,
}

impl Manager {
    fn add(&mut self, surface: impl Surface + 'static) {
        self.storage.push(Box::new(surface));
    }
}

struct SomeOtherStruct {}

struct Obj {
    data: Box<SomeOtherStruct>,
}

impl Obj {
    fn new(some_struct: Box<SomeOtherStruct>) -> Self {
        Obj {
            data: some_struct
        }
    }
}

impl Surface for Obj {}

fn main() {
    let some_struct = SomeOtherStruct{};
    let mut manager = Manager { storage: Vec::new() };

    let obj = Obj::new(Box::new(some_struct));
    manager.add(obj);
}

Playground

我认为这两种解决方案都不错。我不知道哪种解决方案更好,并且我对这种解决方案的优缺点也没有经验。 对我来说(也许是因为我是一个初学者,并且仍然偏爱Rust)所以避免寿命和使用BoxRc等更容易。