Skip Navigation

Is there a way to detect all structs in the current crate that implement a certain trait?

Hi all.

I want to develop a plugin system within my program, and I have a trait that functions defined by plugins should implement.

Currently, my code gets all the functions in a HashMap and then calls them by their name. Problem is, I have to create that hashmap myself by inserting every function myself.

I would really appreciate it if there was a way to say, suppose, all pub members of mod functions:: that implement this trait PluginFunction call register(hashmap) function. So as I add more functions as mod in functions it'll be automatically added on compile.

Pseudocode:

Files:

 undefined
    
src/
├── attrs.rs
├── functions
│   ├── attrs.rs
│   ├── export.rs
│   └── render.rs
├── functions.rs
├── lib.rs

  

Basically, in mod functions I want:

 undefined
    
impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       register_all!(crate::functions::* implementing PluginFunction, &mut functions_map);
       Self { function_map }
  }
}


  

Right now I'm doing:

 undefined
    
impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       crate::functions::attrs::PrintAttr{}.register(&mut functions_map);
       crate::functions::export::ExportCSV{}.register(&mut functions_map);
       crate::functions::render::RenderText{}.register(&mut functions_map);
       // More as I add more functions
       Self { function_map }
  }
}


  
10 comments
  • Getting metadata like this requires adding an extension to the language, either directly in code (as with a macro) or by messing with the build process (running another pass through your code to extract the metadata).

    If you're not adverse to using a global, you could write a macro that you use instead of impl for these traits. So, for example:

     undefined
        
    register_plugin!(impl MyTrait for MyStruct {
      /* regular impl stuff goes here */
    });
    
      

    This macro would populate the global with metadata about the association between MyStruct and MyTrait. You could then pass a copy of it to AllFunctions::new.

    Alternatively, write a macro that just does the registration, which is like what you're already doing:

     undefined
        
    impl MyTrait for MyStruct { ... }
    
    register_plugin!(MyTrait, MyStruct);
    
      

    Another option would be to write something funky in build.rs that does whatever rustdoc does to discover impls and pass that to the rest of your code as const data. As a hack, you could have it invoke rustdoc and then parse the output.

    How dynamic is this plugin system, though? If you can only change the registered plugins by rebuilding, then automatic discovery doesn't seem so warranted. Invoking a simple registration macro at the bottom of each file would save a lot of complexity budget.

10 comments