I want to distinguish some generic containers at runtime using std::any::Any
.
use std::any::Any;
use std::mem::MaybeUninit;
struct Container<T> {
data: [MaybeUninit<T>; 42],
}
impl<T> Container<T> {
pub fn new() -> Self {
let data = unsafe {
MaybeUninit::uninit().assume_init()
};
Container { data }
}
}
struct Thingies {
arr: Vec<Box<dyn Any>>,
}
impl Thingies {
pub fn new() -> Self {
Thingies { arr: Vec::new() }
}
pub fn add<T>(&mut self) {
self.arr.push(Box::new(Container::<T>::new()));
}
pub fn lookup<T>(&mut self) -> Option<&mut Container<T>> {
self.arr.iter_mut().find_map(|e| e.downcast_mut::<Container<T>>())
}
}
fn test<T, U>() {
let mut thingies = Thingies::new();
thingies.add::<T>();
let thingy = thingies.lookup::<T>();
assert!(thingy.is_some());
let thingy2 = thingies.lookup::<U>();
assert!(thingy2.is_none());
}
fn main() {
test::<i32, u32>();
}
This produces an error for each of my add
and lookup
methods like this:
error[E0310]: the parameter type `T` may not live long enough
--> src\main.rs:26:23
|
26 | self.arr.push(Box::new(Container::<T>::new()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
25 | pub fn add<T: 'static>(&mut self) {
| +++++++++
The issue seems to be that a trait object like Box<dyn Trait>
is actually Box<dyn Trait + 'static>
and the suggested fix would "solve" the issue by requiring T: 'static
everywhere which I don't want.
So I tried this tip that suggests adding a lifetime that is different from 'static
but it wouldn't work. When declaring my struct like struct Thingies<'a> { arr: Vec<Box<dyn Any + 'a>> }
I get the following error:
error[E0478]: lifetime bound not satisfied
--> src\main.rs:18:10
|
18 | arr: Vec<Box<dyn Any + 'a>>,
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> src\main.rs:17:17
|
17 | struct Thingies<'a> {
| ^^
= note: but lifetime parameter must outlive the static lifetime
Any
seems to be special in this way because for other traits this strategy obviously works. While I'd like to understand why Any
behaves this way and how to fix it, a solution not involving Any
would also be appreciated if that's at all possible.
You cannot use Any
without 'static
.
It is not "special", it just has an additional constraint. You can see the constraint in its definition:
pub trait Any: 'static {
...
}
Adding + 'a
cannot remove that constraint. That works in the link since dyn Trait
will default to 'static
in certain cases, but that is different than a requirement that it is 'static
.
You cannot use Any
with any type that has non-static lifetimes; it allows you to downcast into concrete types but it cannot distinguish between MyType<'a>
and MyType<'b>
an thus is the reason for the constraint.
I think what you're looking for is not possible. If you want to store references, their lifetimes need to be tracked by the compiler to ensure they stay valid. Even if you associate a lifetime with your Container
and require that types stored outlive that lifetime, that is not enough to guarantee that the type requested from .lookup
would be valid. A type may be stored with a larger lifetime than what is looked-up, which is not ok for some types. See variance. It may be theoretically ok if the type in question was covariant with respect to its associated lifetimes (and many types with lifetimes are covariant), but there is no way to constrain by variance so attempting that would be unsound. The other theoretical way it could work would be to constrain the type matches the lifetime exactly, but that syntax does not exist and likely never will.