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

How to implement a trait with 'static lifetime for a struct with lifetime 'a?

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

第一步我试过这个。

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

这将不起作用,因为 'static'a 之间的生命周期不匹配。换句话说:Surface可以比Obj活得更长,因为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> 来解决这个问题,但这会使事情变得更复杂。

要事第一:

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

对于

来说是冗长的
impl Surface for Obj<'static> {}

您正确地识别了您的问题:

In other words &mut some_struct is lifetime 'a but needs 'static

您需要将 some_struct 声明为 static:

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

问题是,您无法安全地访问可变静态变量,因为它们可以同时突变为多个线程,这是一个问题,因此您需要 unsafe.

所以不,你的代码不是 "Rust like",但恐怕你不能用你当前的架构改变它。


The trait needs to be 'static because I want to store objects from type Surface in a Vec<Box<Surface>>.

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

trait Foo {}
struct Bar;

impl Foo for Bar {}

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

有效并解决了我的问题,但感觉很笨拙并且与 Rust 对抗。

根据你的提示,我找到了一个更好的解决方案,它也可以工作并且不使用 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

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

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

更容易

How to implement a trait with 'static lifetime for a struct with lifetime 'a?

你不会也不可能。 'static 生命周期的目的是说 "something that lives for the entire duration of the program"。没有任意生命周期 'a 满足此要求 除了 'static 本身。