【问题标题】: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; 来解决这个问题,但这会使事情变得更复杂。

【问题讨论】:

    标签: rust traits lifetime


    【解决方案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 等更容易。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多