Rust中如何将meval生成函数存入满足Send+Sync的HashMap?
如何将meval生成的线程安全函数存入HashMap?
问题背景
我通过meval crate在运行时构造函数,希望将其存入一个需要实现Send + Sync且派生了Clone trait的结构体的HashMap中。构造函数的代码如下:
let expr: meval::Expr = "x + y".parse().unwrap(); let func = expr.bind2("x", "y").unwrap(); // 类型为impl Fn(f64, f64) -> f64
结构体定义:
#[derive(Clone)] pub struct MyStruct where Self: Send + Sync, { pub dispatcher: HashMap<String, ???>, }
该结构体必须保持Clone、Send + Sync的约束,暂时无法修改。
尝试的方案及编译错误
我将HashMap的值类型设为Arc<dyn Fn(f64, f64) -> f64 + Send + Sync>,结构体可正常编译,但存入函数时触发错误。代码如下:
type FunctionPointer = Arc<dyn Fn(f64, f64) -> f64 + Send + Sync>; #[derive(Clone)] pub struct DispatcherWrapper where Self: Send + Sync, { pub dispatcher: HashMap<String, FunctionPointer>, } fn foo() { let mut map = HashMap::<String, FunctionPointer>::new(); let expr: meval::Expr = "x + y".parse().unwrap(); let func = expr.bind2("x", "y").unwrap(); // 类型为impl Fn(f64, f64) -> f64 map.insert("foo".to_owned(), Arc::new(func)); }
编译错误信息:
error[E0277]: `Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>` cannot be sent between threads safely --> test.rs:140:34 | 140 | map.insert("foo".to_owned(), Arc::new(func)); | ^^^^^^^^^^^^^^ `Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>` cannot be sent between threads safely | = help: within `(std::string::String, Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>)`, the trait `Send` is not implemented for `Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>`, which is required by `impl Fn(f64, f64) -> f64 + '_: Send` = note: required because it appears within the type `(std::string::String, Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>)` = note: required for `hashbrown::raw::RawTable<(std::string::String, Rc<dyn for<'a> Fn(&'a [f64]) -> Result<f64, FuncEvalError>>)>` to implement `Send`
疑问点
- 无法理解上述错误信息的含义,请解释。
- IDE显示
expr.bind2("x", "y").unwrap()的类型是impl Fn(f64, f64) -> f64,但错误信息中却出现了Fn(&'a [f64]) -> Result<f64, FuncEvalError>的签名,这是为什么?
错误解释与解决方案
错误原因解析
- 核心问题:meval的
bind2方法生成的函数内部使用了Rc智能指针,而Rc未实现Sendtrait(它是单线程引用计数,不支持跨线程传递),导致整个函数无法满足Arc要求的Send + Sync约束。 - 签名不一致的原因:IDE显示的
impl Fn(f64, f64) -> f64是bind2返回的上层包装函数类型——它会将你传入的两个f64参数打包成切片,调用内部的底层函数(类型为Fn(&'a [f64]) -> Result<f64, FuncEvalError>),同时通过unwrap()处理了错误,直接返回f64。但这个上层包装函数持有Rc<dyn ...>,因此无法实现Send。
解决方法
使用meval提供的线程安全绑定方法——bind2_threaded(对应不同参数数量的方法还有bind_threaded、bindn_threaded),这些方法内部用Arc替代Rc,生成的函数天然满足Send + Sync约束。
修改后的代码:
type FunctionPointer = Arc<dyn Fn(f64, f64) -> f64 + Send + Sync>; #[derive(Clone)] pub struct DispatcherWrapper where Self: Send + Sync, { pub dispatcher: HashMap<String, FunctionPointer>, } fn foo() { let mut map = HashMap::<String, FunctionPointer>::new(); let expr: meval::Expr = "x + y".parse().unwrap(); // 替换为bind2_threaded方法 let func = expr.bind2_threaded("x", "y").unwrap(); map.insert("foo".to_owned(), Arc::new(func)); }
这样修改后,生成的函数内部用线程安全的Arc管理状态,满足Send + Sync要求,可正常存入HashMap。
内容的提问来源于stack exchange,提问作者Guillermo
相关产品推荐
相关产品推荐

