Creating shared library in Rust

I would like create a shared library (plugin). I created PluginTrait (interface for library). I don't have access to function write_to_master() after loading by libloading.
Access to functions : start(), stop(), run() is correct.

 //interface

pub trait PluginTrait {
fn write_to_master<T: Any + Debug>(&self, id : u32, value: &T) where Self:Sized;
}

//implementation    
struct Whatever;

impl PluginTrait for Whatever {

	fn write_to_master<T: Any + Debug>(&self, id : u32, value: &T){}


}

#[no_mangle]
pub extern fn object_factory() -> Box<dyn PluginTrait> {
	Box::new(Whatever)
}

use libloading::{Library, Symbol};
use std::env::current_dir;

fn main() {
      let lib = Library::new(path.as_path()).unwrap();
    
        let object_factory: Symbol<extern fn() -> Box<dyn PluginTrait>> = unsafe {
            lib.get(b"object_factory").unwrap()
        };
    
        let obj = object_factory();
    
        let mut api = Api{};
    
        obj.start();
        obj.run(&mut api);
        obj.stop();
    
        //NOT WORKING.. why?
        obj.write_to_master(1, &2);
}

Error:

error: the `write_to_master` method cannot be invoked on a trait object
  --> src/main.rs:29:9
   |
29 |     obj.write_to_master(1, &2);
   |         ^^^^^^^^^^^^^^^

Github repo : https://github.com/hanusek/RustPlugins

write_to_master is a generic function, so when creating the trait object, it isn't known which instantiation of the generic function should be included in the vtable for the trait object. Because of this there is no way to call the function on the trait object. You can fix this by taking value: &dyn AnyAndDebug for write_to_master instead of making it generic and then creating a trait

trait AnyAndDebug {
    fn as_any(&self) -> &dyn Any;
    fn as_debug(&self) -> &dyn Debug;
}

The second change is necessary as &(dyn Any + Debug) can't be casted to &dyn Any or &dyn Debug.

fn write_to_master<T: Any>(&self, id : u32, value: &T) instead fn write_to_master<T: Any + Debug>(&self, id : u32, value: &T) not working.

Hmm... Could You fix my example code? Please.

The problem you're running into is called object safety.

You can use Box<dyn Trait> with shared libraries (and trait objects in general), but not bare T: Trait.

This is because the type T you may call the method with may not have even existed at the time when the library was compiled. You can't expect it to have the method compiled for that specific type.

I can't use generic types in this case?

You can use generic types hidden by a dynamic dispatch Box<dyn>. You can't use generic types monomorphised at compile time (non-dyn types).

This is a hard limitation, because you can't have compile-time generics with a shared library that you're not compiling.

When you have fn foo<T: Trait> the compiler implements it by (a more elegant) equivalent of copy'n'pasting the source code of foo() with the T replaced with an actual type you called it with, and compiling that. With a shared library it doesn't have the source code to recompile the method.

OK. How implement this?
fn write_to_master<Box <dyn T> >(&self, id : u32, value: T)?

fn write_to_master<You can't use this with trait objects at all>()

You need:

fn write_to_master(&self, id: u32, value: Box<dyn Any>) or fn write_to_master(&self, id: u32, value: &dyn Any).

Because you're working with opaque types, there are many limitations. You'll find that dyn supports only one trait at a time, so you'll need to make pub trait AnyDebug: Any + Debug and require dyn AnyDebug if you want both.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.