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);
| ^^^^^^^^^^^^^^^
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
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.
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.
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.