使用good_lp与sea-orm时遇Send trait未实现错误求助
问题描述
我用good_lp crate求解线性规划问题,之后用异步框架sea-orm把结果存到数据库里,代码如下:
#[debug_handler] pub async fn compute( Extension(database): Extension<DatabaseConnection>, ) -> Html<String> { // some computations for outcome_name in outcome_names.iter() { // set up problem let result = p.solve(); if let Ok(solution) = result { for (intervention_name, var) in problem.var_map.iter() { let updated_value = solution.value(var.clone()) as i32; let updated_size = value_to_size.get(&updated_value).unwrap().to_string(); update_intervention_outcome( &database, intervention_name.clone(), outcome_name.clone(), updated_size.clone(), ).await; } }; }; // return html }
现在遇到的问题是求解结果CoinCbcSolution没实现Send trait,不清楚为什么solution的销毁会引发问题,也不知道怎么修复。错误信息如下:
error: future cannot be sent between threads safely --> src/routes/computations.rs:76:1 | 76 | #[debug_handler] | ^^^^^^^^^^^^^^^^ future returned by `compute` is not `Send` | = help: within `impl futures::Future<Output = axum::response::Html<std::string::String>>`, the trait `std::marker::Send` is not implemented for `*mut coin_cbc_sys::Cbc_Model` note: future is not `Send` as this value is used across an await --> src/routes/computations.rs:341:126 | 336 | if let Ok(solution) = result { | -------- has type `CoinCbcSolution` which is not `Send` ... 341 | update_intervention_outcome( &database, intervention_name.clone(), outcome_name.clone(), updated_size.clone(), ).await; | ^^^^^^ await occurs here, with `solution` maybe used later 342 | } 343 | }; | - `solution` is later dropped here
解决方案
问题根源
CoinCbcSolution内部持有未实现Send的原始指针(*mut coin_cbc_sys::Cbc_Model),而Axum的handler要求返回的Future必须是Send的——因为异步运行时会在不同线程间调度Future。你的代码里solution在await之后才被销毁,导致Future持有了非Send的solution,违反了约束。
修复方法
把从solution中提取数据的操作和异步数据库操作分开:先一次性把所有需要的数据从solution里取出来,存到一个实现了Send的结构里(比如包含String的元组Vec),等solution被销毁后,再执行异步的数据库更新。
修改后的代码示例:
#[debug_handler] pub async fn compute( Extension(database): Extension<DatabaseConnection>, ) -> Html<String> { // some computations for outcome_name in outcome_names.iter() { // set up problem let result = p.solve(); if let Ok(solution) = result { // 先收集所有需要更新的数据,此时solution还在作用域内 let updates: Vec<_> = problem.var_map.iter() .map(|(intervention_name, var)| { let updated_value = solution.value(var.clone()) as i32; let updated_size = value_to_size.get(&updated_value).unwrap().to_string(); ( intervention_name.clone(), outcome_name.clone(), updated_size, ) }) .collect(); // 这里solution已经被销毁,接下来的异步操作不会持有非Send的对象 for (intervention_name, outcome_name, updated_size) in updates { update_intervention_outcome( &database, intervention_name, outcome_name, updated_size, ).await; } }; }; // return html }
原理说明
通过先收集所有需要的数据,solution会在collect()之后、进入异步循环之前被销毁,这样后续的异步Future就不会持有任何非Send的对象,满足Axum对Future的Send要求。
内容的提问来源于stack exchange,提问作者bard
相关产品推荐
相关产品推荐

