Reputation: 1612
I have a trait Surface: 'static
that I want to implement for a struct Obj<'a>
. The trait needs to be 'static
because I want to store objects from type Surface
in a Vec<Box<Surface>>
.
In the first step I tried this.
impl<'a> Surface for Obj<'a> {}
This will not work because of a lifetime mismatch between 'static
and 'a
. In other words: Surface
can live longer than Obj
because Surface
is 'static
.
I changed my implementation as follows.
impl<'a> Surface for Obj<'a> where 'a: 'static {}
As far as I understand the documentation correctly, what I'm doing is, 'a
can outlive 'static
. Do I want this?
If I transfer the ownership of Obj<'a>
, the compiler tells me that a mutable reference inside Obj
will not live long enough and is still borrowed.
Here is a short example.
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);
}
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
In other words &mut some_struct
is lifetime 'a
but needs 'static
. Ok it's clear because some_struct
lives in Obj<'a>
so it cannot be 'static
?
Is this what I'm trying to do "Rust like"? I've no idea how to get it to work. Its really confusing with the lifetimes. I think I can get around this by using a Rc<T>
, but this will make things more complex.
Upvotes: 5
Views: 6079
Reputation: 430534
How to implement a trait with
'static
lifetime for a struct with lifetime'a
?
You do not and cannot. The purpose of the 'static
lifetime is to say "something that lives for the entire duration of the program". No arbitrary lifetime 'a
meets this requirement except 'static
itself.
Upvotes: 5
Reputation: 1612
@hellow's answer works and solves my problem, but it feels hacky and working against Rust.
With your hints, I found a better solution that alsos work and does not use unsafe
.
Solution 1
I specified explicit lifetime parameters for the Manager
and for the type 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);
}
Solution 2
Store a Box<SomeOtherStruct>
instead of &mut SomeOtherStruct
in Obj
. This will eliminate the lifetimes:
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);
}
In my opinion both solutions are good. I don't know which solution is better and I've no experience with pro and cons of this solutions.
For me (maybe because I'm a beginner and still leaning Rust) it's easier to avoid lifetimes and use Box
, Rc
etc.
Upvotes: 3
Reputation: 13420
First things first:
impl<'a> Surface for Obj<'a> where 'a: 'static {}
is verbose for
impl Surface for Obj<'static> {}
You identified your problem correctly:
In other words
&mut some_struct
is lifetime'a
but needs'static
You need to declare some_struct
as static
:
fn main() {
static mut SOME_STRUCT: SomeOtherStruct = SomeOtherStruct {};
// ...
let obj = unsafe { Obj::new(&mut SOME_STRUCT) };
// ...
}
The problem is, that you cannot access mutable static variables safely, because they can be mutated be multiple thread simultaneously and that is a problem, hence you need unsafe
.
So no, your code is not "Rust like", but I'm afraid you can't change that with your current architecture.
The trait needs to be 'static because I want to store objects from type
Surface
in aVec<Box<Surface>>
.
I don't get why you think you need 'static
in the first place, e.g. this code is perfectly legal:
trait Foo {}
struct Bar;
impl Foo for Bar {}
fn main() {
let b: Box<Foo> = Box::new(Bar);
}
Upvotes: 3