How to ship multiple plugin implementations in one cdylib¶
This guide shows how to put two or more #[plugin_impl] blocks in the same
Rust cdylib so a single .dylib / .so / .dll exposes multiple plugins to
the host.
Prerequisites¶
- A Fidius interface crate with a
#[plugin_interface]trait (see How to scaffold a project) - Familiarity with
#[plugin_impl]for a single plugin
1. Define the interface (once)¶
Define your interface trait in a shared crate as usual:
#[fidius::plugin_interface(version = 1, buffer = PluginAllocated)]
pub trait Greeter: Send + Sync {
fn greet(&self, name: String) -> String;
}
2. Write multiple implementations in one lib.rs¶
In your plugin cdylib crate, implement the trait on as many structs as you
need. Apply #[plugin_impl(TraitName)] to each:
use my_interface::{plugin_impl, Greeter};
// --- Plugin 1 ---
pub struct HelloGreeter;
#[plugin_impl(Greeter)]
impl Greeter for HelloGreeter {
fn greet(&self, name: String) -> String {
format!("Hello, {}!", name)
}
}
// --- Plugin 2 ---
pub struct GoodbyeGreeter;
#[plugin_impl(Greeter)]
impl Greeter for GoodbyeGreeter {
fn greet(&self, name: String) -> String {
format!("Goodbye, {}!", name)
}
}
// Emit the combined registry (once, at the end)
fidius_core::fidius_plugin_registry!();
Each #[plugin_impl] generates:
- A static instance of the struct
extern "C"shim functions for every method- A
free_bufferfunction - A static
PluginDescriptor - An
inventory::submit!call that registers the descriptor
3. Host-side discovery¶
On the host side, PluginHost::discover() scans search paths for dylib files,
loads each one, and iterates through all plugins in the registry:
use fidius_host::host::PluginHost;
let host = PluginHost::builder()
.search_path("./plugins")
.build()?;
let plugins = host.discover()?;
// plugins is Vec<PluginInfo> -- may contain multiple entries from one dylib
for info in &plugins {
println!("{} implements {}", info.name, info.interface_name);
}
Each PluginInfo contains the plugin name, interface name, interface hash,
version, capabilities, and buffer strategy. Two plugins from the same dylib
will share the same interface hash and version but have different names
(e.g., "HelloGreeter" and "GoodbyeGreeter").
To load a specific plugin by name:
4. Verify with fidius inspect¶
After building, confirm that both plugins are visible:
$ fidius inspect target/release/libmy_plugins.dylib
Plugin Registry: target/release/libmy_plugins.dylib
Plugins: 2
[0] HelloGreeter
Interface: Greeter
Interface hash: 0x...
...
[1] GoodbyeGreeter
Interface: Greeter
Interface hash: 0x...
...
See How to inspect a plugin for full details on the inspect output.
Things to keep in mind¶
- You can implement different interface traits in the same dylib, not just
multiple implementations of one trait. Each
#[plugin_impl]is independent. - Call
fidius_plugin_registry!()exactly once per cdylib, typically at the bottom oflib.rs. - The
plugin_countfield in the registry reflects the total number of#[plugin_impl]blocks, regardless of how many interfaces are involved. - If the
asyncfeature is enabled, all plugins in the dylib share a single tokio runtime (see How to add async methods).
See also¶
- How to scaffold a project -- generate the initial crate structure
- How to add async methods -- async works the same way with multiple plugins
- How to inspect a plugin -- verify plugin count and metadata after building