Rust插件库Cactus的使用:高效、灵活的插件系统扩展,提升Rust应用模块化开发能力
Rust插件库Cactus的使用:高效、灵活的插件系统扩展,提升Rust应用模块化开发能力
Cactus库提供了一个不可变的仙人掌栈(也称为意大利面条栈或父指针树)。仙人掌栈是一个(可能为空的)节点,带有一个(可能为空的)指向父节点的指针。任何给定节点都有一个返回根节点的唯一路径。与其可变地更新栈,不如创建和获取对不可变节点的访问(当节点变得不可达时,其内存会自动回收)。
基本使用示例
use cactus::Cactus;
let c = Cactus::new();
assert!(c.is_empty());
let c2 = c.child(1);
assert_eq!(c2.len(), 1);
assert_eq!(*c2.val().unwrap(), 1);
let c3 = c2.parent().unwrap();
assert!(c3.is_empty());
创建多个子栈示例
use cactus::Cactus;
let c = Cactus::new().child(1);
let c2 = c.child(2);
let c3 = c.child(3);
assert!(c2 != c3);
assert_eq!(c2.vals().cloned().collect::<Vec<_>>(), [2, 1]);
assert_eq!(c3.vals().cloned().collect::<Vec<_>>(), [3, 1]);
完整示例代码
下面是一个更完整的示例,展示了如何使用Cactus库来构建一个简单的插件系统:
use cactus::Cactus;
// 定义插件特征
trait Plugin {
fn name(&self) -> &str;
fn execute(&self);
}
// 实现一个具体插件
struct GreetPlugin;
impl Plugin for GreetPlugin {
fn name(&self) -> &str {
"greet"
}
fn execute(&self) {
println!("Hello from GreetPlugin!");
}
}
// 实现另一个插件
struct LogPlugin;
impl Plugin for LogPlugin {
fn name(&self) &str {
"log"
}
fn execute(&self) {
println!("Logging from LogPlugin");
}
}
fn main() {
// 创建空的插件栈
let plugin_stack = Cactus::new();
// 添加插件
let with_greet = plugin_stack.child(GreetPlugin);
let with_log = with_greet.child(LogPlugin);
// 遍历并执行所有插件
for plugin in with_log.vals() {
plugin.execute();
}
// 输出:
// Logging from LogPlugin
// Hello from GreetPlugin!
}
更复杂的插件系统示例
use cactus::Cactus;
use std::any::{Any, TypeId};
// 定义插件管理器和特征
struct PluginManager {
plugins: Cactus<Box<dyn Any>>,
}
impl PluginManager {
fn new() -> Self {
Self {
plugins: Cactus::new(),
}
}
fn add_plugin<T: 'static>(&self, plugin: T) -> Self {
Self {
plugins: self.plugins.child(Box::new(plugin)),
}
}
fn get_plugin<T: 'static>(&self) -> Option<&T> {
for plugin in self.plugins.vals() {
if plugin.is::<T>() {
return plugin.downcast_ref::<T>();
}
}
None
}
}
// 定义几个插件类型
struct DatabasePlugin {
connection: String,
}
struct AuthPlugin {
token: String,
}
fn main() {
let manager = PluginManager::new()
.add_plugin(DatabasePlugin {
connection: "localhost".to_string(),
})
.add_plugin(AuthPlugin {
token: "secret".to_string(),
});
// 获取并使用插件
if let Some(db) = manager.get_plugin::<DatabasePlugin>() {
println!("Database connection: {}", db.connection);
}
if let Some(auth) = manager.get_plugin::<AuthPlugin>() {
println!("Auth token: {}", auth.token);
}
}
这些示例展示了如何使用Cactus库来构建灵活、可扩展的插件系统,使Rust应用具有更好的模块化开发能力。通过不可变的数据结构和父子关系,可以安全地管理插件生命周期和依赖关系。
完整示例Demo
基于上述内容,下面是一个更完整的Cactus插件系统实现示例,展示了如何在实际项目中使用:
use cactus::Cactus;
use std::any::{Any, TypeId};
// 定义插件特征
trait Plugin: Any {
fn name(&self) -> &str;
fn init(&self);
fn execute(&self, context: &mut PluginContext);
}
// 插件上下文
struct PluginContext {
data: String,
}
// 具体插件实现 - 日志插件
struct LoggerPlugin {
log_level: String,
}
impl Plugin for LoggerPlugin {
fn name(&self) -> &str {
"logger"
}
fn init(&self) {
println!("Initializing LoggerPlugin with level: {}", self.log_level);
}
fn execute(&self, context: &mut PluginContext) {
println!("[{}] Processing: {}", self.log_level, context.data);
context.data = context.data.to_uppercase();
}
}
// 具体插件实现 - 验证插件
struct ValidationPlugin;
impl Plugin for ValidationPlugin {
fn name(&self) -> &str {
"validator"
}
fn init(&self) {
println!("Initializing ValidationPlugin");
}
fn execute(&self, context: &mut PluginContext) {
if context.data.is_empty() {
println!("Validation error: empty input");
}
}
}
// 插件管理器
struct PluginSystem {
plugins: Cactus<Box<dyn Plugin>>,
}
impl PluginSystem {
fn new() -> Self {
Self {
plugins: Cactus::new(),
}
}
fn add_plugin<P: Plugin + 'static>(mut self, plugin: P) -> Self {
println!("Adding plugin: {}", plugin.name());
plugin.init();
Self {
plugins: self.plugins.child(Box::new(plugin)),
}
}
fn execute_all(&self, mut context: PluginContext) -> PluginContext {
for plugin in self.plugins.vals() {
plugin.execute(&mut context);
}
context
}
}
fn main() {
// 创建插件系统并添加插件
let system = PluginSystem::new()
.add_plugin(LoggerPlugin {
log_level: "INFO".to_string(),
})
.add_plugin(ValidationPlugin);
// 创建执行上下文
let mut context = PluginContext {
data: "hello world".to_string(),
};
// 执行所有插件
context = system.execute_all(context);
println!("Final result: {}", context.data);
// 输出:
// Adding plugin: logger
// Initializing LoggerPlugin with level: INFO
// Adding plugin: validator
// Initializing ValidationPlugin
// [INFO] Processing: hello world
// Final result: HELLO WORLD
}
1 回复
Rust插件库Cactus使用指南
Cactus简介
Cactus是一个高效、灵活的Rust插件系统库,旨在提升Rust应用的模块化开发能力。它提供了轻量级的插件架构,允许开发者动态加载和卸载功能模块,而无需重新编译主应用程序。
主要特性
- 动态加载:支持运行时加载和卸载插件
- 类型安全:利用Rust的类型系统保证插件接口安全
- 跨平台:支持Windows、Linux和macOS
- 低开销:设计注重性能,插件调用开销小
- 灵活扩展:易于集成到现有项目中
安装方法
在Cargo.toml中添加依赖:
[dependencies]
cactus = "0.3"
完整示例demo
1. 创建接口库 (plugins-interface)
// plugins-interface/src/lib.rs
use cactus::Plugin;
#[derive(Plugin)]
pub trait Greeter {
fn greet(&self, name: &str) -> String;
}
#[derive(Plugin)]
pub trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
2. 实现插件 (plugin-hello)
// plugin-hello/src/lib.rs
use plugins_interface::{Greeter, Calculator};
#[derive(Default)]
pub struct HelloPlugin;
impl Greeter for HelloPlugin {
fn greet(&self, name: &str) -> String {
format!("Hello, {}! Welcome to Cactus plugin system.", name)
}
}
impl Calculator for HelloPlugin {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
// 必须导出插件创建函数
#[no_mangle]
pub extern "C" fn _plugin_create() -> *mut () {
let plugin = Box::new(HelloPlugin::default());
Box::into_raw(plugin) as *mut _
}
3. 主程序 (my-app)
// my-app/src/main.rs
use cactus::{PluginLoader, PluginManager};
use plugins_interface::{Greeter, Calculator};
fn main() {
// 1. 使用PluginLoader加载单个插件
println!("=== 使用PluginLoader ===");
let mut loader = PluginLoader::new();
// 根据平台加载插件
#[cfg(target_os = "linux")]
let plugin_path = "./target/debug/libplugin_hello.so";
#[cfg(target_os = "windows")]
let plugin_path = "./target/debug/plugin_hello.dll";
#[cfg(target_os = "macos")]
let plugin_path = "./target/debug/libplugin_hello.dylib";
loader.load(plugin_path).expect("加载插件失败");
// 使用Greeter功能
let greeter = loader.get::<dyn Greeter>().expect("获取Greeter失败");
println!("{}", greeter.greet("Developer"));
// 使用Calculator功能
let calculator = loader.get::<dyn Calculator>().expect("获取Calculator失败");
println!("5 + 3 = {}", calculator.add(5, 3));
println!("5 * 3 = {}", calculator.multiply(5, 3));
loader.unload().expect("卸载插件失败");
// 2. 使用PluginManager管理多个插件
println!("\n=== 使用PluginManager ===");
let mut manager = PluginManager::new();
// 加载多个插件
manager.load_all(vec![plugin_path]).expect("加载插件失败");
// 获取并使用插件
if let Some(greeter) = manager.get::<dyn Greeter>("hello") {
println!("{}", greeter.greet("Manager User"));
}
if let Some(calc) = manager.get::<dyn Calculator>("hello") {
println!("10 + 20 = {}", calc.add(10, 20));
}
// 卸载所有插件
manager.unload_all().expect("卸载插件失败");
}
4. 项目配置文件
接口库Cargo.toml
[package]
name = "plugins-interface"
version = "0.1.0"
edition = "2021"
[dependencies]
cactus = "0.3"
插件库Cargo.toml
[package]
name = "plugin-hello"
version = "0.1.0"
edition = "2021"
crate-type = ["cdylib"]
[dependencies]
plugins-interface = { path = "../interface" }
cactus = "0.3"
主程序Cargo.toml
[package]
name = "my-app"
version = "0.1.0"
edition = "2021"
[dependencies]
cactus = "0.3"
plugins-interface = { path = "./plugins/interface" }
构建和运行步骤
- 先构建接口库:
cd plugins/interface
cargo build
- 构建插件库:
cd ../hello
cargo build
- 运行主程序:
cd ../../my-app
cargo run
注意事项
- 插件和主程序必须使用相同版本的Rust编译器
- 在Windows上需要注意动态链接库的依赖关系
- 插件卸载后不应再使用其提供的任何对象
- 跨插件通信应通过主程序进行