Property do not live enough while trying to implement extractor

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 :

Try using a HRTB on T instead of using a generic lifetime 'a. The reference you get through the read guard does not live for 'a but only for as long as the read guard isn't dropped.

pub fn extract<T>(&self) -> Result<T, String>
where
    for<'a> T: TryFrom<Reflects<'a>>,

Playground.

4 Likes

Amazing ! I have so many things to learn about lifetimes ! Thanks.

1 Like