In this lesson, you will learn why generic code cannot be exported directly across dynamic library boundaries and how to use trait objects to bridge the gap.
The Problem: The "Monomorphization" Barrier
When you write a generic function like fn process<T: Trait>(item: T), the Rust compiler performs monomorphization. It creates a specialized copy of that function for every concrete type you use.
However, a dynamic library (a .so or .dll file) is compiled independently of your main application. If you try to export a generic function, the compiler doesn't know which concrete types your future application will use. Consequently, it cannot generate the code for those types inside the library. Because the machine code for the specific implementation doesn't exist yet, you cannot simply "load" a generic function from a dynamic library.
The Solution: Erasing Types at the Boundary
To load code dynamically, we must switch from static dispatch (compile-time generic selection) to dynamic dispatch (runtime selection via dyn Trait).
Instead of exporting a generic function, you export a function that returns a trait object. By "erasing" the specific generic type T and returning a Box<dyn MyTrait>, you provide a stable interface that the dynamic library can actually implement.
// In the dynamic library
pub trait Processor {
fn run(&self);
}
// We don't export a generic function.
// We export a factory that returns a concrete type implementing the trait.
#[no_mangle]
pub extern "C" fn create_processor() -> Box<dyn Processor> {
Box::new(MyConcreteType)
}
By wrapping the logic in a non-generic trait object, you hide the complexity of the generic implementation from the ABI (Application Binary Interface). The main application doesn't need to know the specific type; it only needs to know that the object satisfies Processor.
This technique requires careful memory management, as the dynamic library and the main binary must share the same allocator to safely pass Box pointers across the boundary.
Key Takeaway: Because generic code relies on compile-time monomorphization, you must use trait objects to erase specific types before crossing dynamic library boundaries.