You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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`

疑问点

  1. 无法理解上述错误信息的含义,请解释。
  2. IDE显示expr.bind2("x", "y").unwrap()的类型是impl Fn(f64, f64) -> f64,但错误信息中却出现了Fn(&'a [f64]) -> Result<f64, FuncEvalError>的签名,这是为什么?

错误解释与解决方案

错误原因解析

  • 核心问题:meval的bind2方法生成的函数内部使用了Rc智能指针,而Rc未实现Send trait(它是单线程引用计数,不支持跨线程传递),导致整个函数无法满足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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:44:54