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

Posted

技术标签:

【中文标题】如何为具有生命周期\'a的结构实现具有\'静态生命周期的特征?【英文标题】:How to implement a trait with 'static lifetime for a struct with lifetime 'a?如何为具有生命周期'a的结构实现具有'静态生命周期的特征? 【发布时间】:2019-08-05 00:32:03 【问题描述】:

我有一个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&lt;'a&gt; 的所有权,编译器会告诉我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

换句话说&amp;mut some_struct 是生命周期'a 但需要'static。好的很清楚,因为some_struct 住在Obj&lt;'a&gt; 所以它不能是'static

这就是我想要做的“Rust like”吗?我不知道如何让它工作。它真的与生命相混淆。我想我可以通过使用Rc&lt;T&gt; 来解决这个问题,但这会使事情变得更复杂。

【问题讨论】:

【参考方案1】:

如何为生命周期为'a 的结构实现一个生命周期为'static 的特征?

你没有也不能。 'static 生命周期的目的是说“在整个程序期间都存在的东西”。没有任意生命周期 'a 满足此要求除了 'static 本身。

【讨论】:

【参考方案2】:

第一件事:

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

详细说明

impl Surface for Obj<'static> 

你正确地发现了你的问题:

换句话说&amp;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 一样”,但恐怕你不能用你当前的架构来改变它。


特征需要是“静态的”,因为我想将 Surface 类型的对象存储在 Vec&lt;Box&lt;Surface&gt;&gt; 中。

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

trait Foo 
struct Bar;

impl Foo for Bar 

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

【讨论】:

好的...首先感谢您的回答。那不是我想要的。我想要“生锈的方式”。你能告诉我生锈的方法吗,或者给我一个提示,我可以单独弄清楚。我做了我的特征'static,因为这个签名fn add(&amp;mut self, surface: impl Surface) 上的编译器错误没有'static,编译器会抛出一个错误,但感谢提示我将其更改如下:fn add(&amp;mut self, surface: impl Surface + 'static)。现在我可以从特征中删除 'static。但问题仍然存在。如果我必须重构我的架构,以 rust 的方式归档,没问题,我正在学习。【参考方案3】:

@hellow's answer 工作并解决了我的问题,但感觉很hacky 并且与Rust 对抗。

根据您的提示,我找到了一个更好的解决方案,它也可以工作并且不使用unsafe

解决方案 1

我为ManagerBox&lt;Surface + 'a&gt; 类型指定了显式生命周期参数:

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&lt;SomeOtherStruct&gt; 而不是&amp;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 等更容易。

【讨论】:

以上是关于如何为具有生命周期'a的结构实现具有'静态生命周期的特征?的主要内容,如果未能解决你的问题,请参考以下文章

了解生命周期:最大生命周期和“静态”

如何为闭包参数声明生命周期?

具有持久 HTTP 连接的 IDbConnection 生命周期管理

c++类中 各种成员的生命周期?

如何为不同环境的 SwiftUI App 生命周期应用程序运行 UI 测试?

C语言中,哪种存储类的作用域与生命周期是不一致的?