(规则问题、无法编译、限制)可变struct中的闭包参数使用struct本身、挑战可变借用多次无法编译
(规则问题、无法编译、限制)可变struct中的闭包参数使用struct本身、挑战可变借用多次无法编译
- 作者:祐祐
- 发布时间:2024-08-16 23:52:00
- 链接:https://rustcc.cn/article?id=2f8aef75-c664-4059-afc3-22df40f73b75
- 标签:可变借用,限制,语法,struct,闭包
问题描述
希望实现一个解释器,将Rust中的函数绑定到解释器的全域上下文cx
中,并让从cx
中获取的target_func
能够将cx
自身作为参数传入。但在实现过程中遇到编译错误,以下是简化的代码示例:
use std::collections::HashMap;
struct Content {
pub data: i32,
pub map_env_fun: HashMap<String, EnvFunction>,
}
impl Content {
fn new() -> Self {
Content {
data: 0,
map_env_fun: HashMap::new(),
}
}
}
type BoxFnMut = Box<dyn FnMut(&mut Content) -> bool>;
enum EnvFunction {
Func(BoxFnMut),
}
fn __fun(cx: &mut Content) -> bool {
cx.data += 1;
true
}
fn init_built_in_functions(cx: &mut Content) {
let fun: BoxFnMut = Box::new(__fun);
cx.map_env_fun
.insert("key_1".to_string(), EnvFunction::Func(fun));
cx.data += 1;
}
fn main() {
let mut cx = Content::new();
init_built_in_functions(&mut cx);
let EnvFunction::Func(target_func) = cx.map_env_fun.get_mut("key_1").unwrap();
// --------------------------------------------------------------
target_func(&mut cx); // Error:编译失败
// --------------------------------------------------------------
println!("Content data: {}", cx.data);
}
讨论与解决方案
代码优化提示
用户small-white0-0
指出,使用cargo clippy
可以简化代码:
let EnvFunction::Func(target_func) = cx.map_env_fun
.get_mut("key_1")
.map(clone) // 替代 map(|f| f.clone())
.unwrap();
方案1:使用函数指针(FnPtr
)
用户elsejj
提出使用函数指针避免闭包的借用问题:
type FuncPtr = fn(&mut Content) -> bool;
enum EnvFunction {
Func(FuncPtr), // 函数指针可复制,避免借用冲突
}
方案2:使用RefCell
实现内部可变性
用户zylthinking
分享了使用RefCell
的方案,通过运行时借用检查绕过编译期限制:
use std::cell::RefCell;
use std::collections::HashMap;
struct Content {
pub data_i32: RefCell<i32>,
pub data_str: RefCell<String>,
pub map_env_fun: RefCell<HashMap<String, EnvFunction>>,
}
type BoxFnMut = Box<dyn Fn(&Content) -> bool>;
enum EnvFunction {
Func(BoxFnMut),
}
// 使用宏简化重复的借用逻辑
macro_rules! get_dyn_hashmap_func {
($name:ident, $cx:expr, $key:expr) => {
let map_env_fun = $cx.map_env_fun.borrow();
let EnvFunction::Func(target_func_1) = map_env_fun.get($key).unwrap();
let $name = target_func_1;
};
}
方案3:使用Arc<Rc>
共享闭包
用户small-white0-0
提出使用引用计数智能指针Arc
实现闭包的共享,避免所有权转移问题:
use std::collections::HashMap;
use std::sync::Arc;
type ARrcFn = Arc<dyn Fn(&mut Content) -> bool>;
#[derive(Clone)]
enum EnvFunction {
Func(ARrcFn), // 支持克隆,允许多次安全借用
}
fn init_built_in_functions(cx: &mut Content) {
let fun: ARrcFn = Arc::new(__fun);
cx.map_env_fun.insert("key_1".to_string(), EnvFunction::Func(fun));
}
fn main() {
let mut cx = Content::new();
init_built_in_functions(&mut cx);
let EnvFunction::Func(target_func) = cx.map_env_fun.get_mut("key_1").map(|f| f.clone()).unwrap();
target_func(&mut cx); // 编译通过
}
其他讨论点
- 性能与安全性权衡:
RefCell
将借用检查推迟到运行时,可能导致panic
;Arc
在单线程中可使用Rc
,兼顾安全与性能。 - 函数复用需求:若需要多次调用
map
中的函数,需避免通过remove
取出后再插入的临时方案(如let EnvFunction::Func(mut target_func) = cx.map_env_fun.remove("key_1").unwrap();
),应优先使用共享指针。 - 内置函数特性:若函数是静态的且不捕获环境,直接使用函数指针是最优解;若需要动态闭包,需通过智能指针或内部可变性解决借用冲突。
总结
通过引入函数指针、RefCell
或Arc/Rc
等智能指针,结合Rust的借用规则,可以有效解决可变struct
中闭包参数使用自身时的编译错误。具体方案需根据场景权衡安全性、性能和代码复杂度,优先避免unsafe
代码,合理利用Rust的类型系统特性。
本文是转载文章,版权归原作者所有。建议访问原文,转载本文请联系原作者。
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果