Hi !
I'm trying to implement a way to extract typed things behind trait/Box abstractions.
To resume that, I have a struct Daemon
which own a vector of Actor
(trait behiond boxes). I would like to be able to find and return typed "things" from this list of Actor
.
My idea is to use an enum intermediate (Reflects
), and declare implementation of TryFrom<Reflects>
for my "things". Important things is to that TryFrom<Reflects>
implementation don't cost CPU (so contain references to be able to clone only when type match). Because, Daemon
will try on each Actor
to see if able to produce this "thing".
There is the implementation (the "thing" here will be a Tree
struct):
use std::sync::RwLock;
trait Actor {
fn reflects(&self) -> Reflects;
}
struct MyTreeActor {
tree: Tree
}
impl Actor for MyTreeActor {
fn reflects(&self) -> Reflects {
Reflects::Tree(&self.tree)
}
}
#[derive(Debug, Clone)]
enum Reflects<'a> {
Tree(&'a Tree),
SomethingElse(&'a i32),
}
#[derive(Debug, Clone)]
struct Tree;
impl<'a> TryFrom<Reflects<'a>> for Tree {
type Error = String;
fn try_from(value: Reflects<'a>) -> Result<Self, Self::Error> {
if let Reflects::Tree(v) = value {
return Ok((*v).clone());
}
Err("NotFound".to_string())
}
}
struct Daemon {
actors: RwLock<Vec<Box<dyn Actor>>>,
}
impl Daemon {
pub fn extract<'a, T: TryFrom<Reflects<'a>>>(&'a self) -> Result<T, String> {
let mut resp = Err("NotFound".to_string());
let actors = self.actors.read().unwrap();
for actor in actors.iter() {
let reflects = actor.reflects();
if let Ok(v) = T::try_from(reflects.clone()) {
resp = Ok(v)
}
}
resp
}
}
fn main() {
let daemon = Daemon { actors: RwLock::new(vec![Box::new(MyTreeActor{tree: Tree})]) };
let tree: Tree = daemon.extract().unwrap();
dbg!(tree);
}
This code don't compile:
error[E0597]: `actors` does not live long enough
--> src/main.rs:49:22
|
45 | pub fn extract<'a, T: TryFrom<Reflects<'a>>>(&'a self) -> Result<T, String> {
| -- lifetime `'a` defined here
46 | let mut resp = Err("NotFound".to_string());
47 | let actors = self.actors.read().unwrap();
| ------ binding `actors` declared here
48 |
49 | for actor in actors.iter() {
| ^^^^^^-------
| |
| borrowed value does not live long enough
| argument requires that `actors` is borrowed for `'a`
...
57 | }
| - `actors` dropped here while still borrowed
That's (as I understand) because when (in Deamon.extract
) we T::try_from(reflects.clone())
, compiler can't know we will clone "thing" inside the TryFrom
implement. So, it considers Ok(v)
can contain the reference linked on actors
.
Is there a way to prevent that ? A safe type or (why not) an unsafe way (I never did unsafe) ?
Or, maybe, it exists another way to reach my original goal ? Thanks a lot in advance :