救各路大神解答,
目的:我想要做一个解释器,有一个需求就是要从 rust 中的函数绑掉到给解释器用,作为解释器的全域上下文。
问题:我想要让从cx
捞出来的target_func
可以在将cx
传入target_func
作为其上下文,但是我怎实现都做不出来,请问下面这段程式怎么让编译通过。
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);
}
Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=072c17bf78b0cdb99dfa33e408007a7b
评论区
写评论学习了,提一个小点:
这里cargo clippy 会提示你,
map(|f| f.clone())
可以简化成map(clone)
我之前也遇到过这个问题。 用函数指针解决的。
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=602719b3d787e684de28108660e2af49
都是些基本的概念,没什么特别的技巧,也无需客气,多看多写,与君共勉。
--
👇
祐祐
e神您好,这边有点抱歉阿!一个没看清楚的误会~ 再来是真的很感谢你提供的思路,多亏了有你的帮助我已将您提供的方法合并到我的专案代码里了。
如果你这个实现,与我的实现有什么想深入探讨的地方,非常欢迎你的指教。
非常的感谢你(^ _ ^ ~)。
-- 👇 elsejj
谢谢你提出问题,我竟然没发现弄错了,阿真的太困了~~
指针的实现也是很特别,感谢你提供的想法~
--
👇
small-white0-0
很高兴我的回复对你有帮助。不过,
Arc
的智能指针的方案,是 elsejj 提出的,我受之有愧。你的
RefCell
的方案,也很好。内部可变性配合不可变引用,也是个好思路,学到了。--
👇
祐祐
关于你提到的我有想过,这在日后会损失相当大的灵活性,
__func
不能完整取用cx: &Content
,或许几个月后真的不需要这样的设计会考虑这样的实现。--
👇
zylthinking
确实开始很失望,不过看到你的实现,真的非常开心,非常感谢你。
我昨晚上一直都在弄这个,实现了一个
RefCell
版本(而外话我弄超久从Rc<RefCell>
演变到单个RefCell
这样,一直到清晨),刚刚也还在研究你提供的第三个的版本,至于你提供的指标版很有趣本我在研究看看,只是来弄rust 通常就是不想有unsafe 代码所以先不考虑。我后来跑了两个版本的测试,不是很严谨的基准测试,和你的实现性能相差不远,但你的版本更好。
我也稍微整理一下你提供的范例,然后方便比较~
我也把我的实现也丢上来。
我在研究这两个版本,这两个版本间的差异,是否有什么条件限制。
这边整理一下你提供的范例方便比较~
--
👇
small-white0-0: 首先恢复一下你的信心。rust的借用规则的选择是为了安全性牺牲了部分安全的使用场景。而你的方案涉及了多个借用问题,这个被借用规则拒绝也很正常。方法当然是有的,不过就看你选择是花你的时间还是机器时间了。
解决方法
方案1
高安全但是低性能。(花机器时间)
获取函数的时候,就不要使用引用了,改为使用所有权。我在学异步运行时实现时,看到的教程都是对
cx
用一次clone
一次。所以,你这里只是克隆函数又不上下文cx
,问题不大。方案2
高性能但是低安全。(花费大量自己时间)
使用裸指针,这样就没有了
cx
的二次借用问题。但是 安全自负!!! 你需要在每一处使用裸指针的地方保证指针有效性。https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c65470075352c10dd94083a0fe573fef
方案3
高性能+相对安全。(花微量自己时间)
该方案就是在方案2的基础上封装了一下。限制函数裸指针只会在一处地方使用。
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1f82fd96db17e8c2c20930ac589b09cf
或者这样:
若这些 builtin method 真的是内置语义, 是不是意味着
若是1, 则静态分发就行了, 反正 key_1 也是静态编码的 若是2, 则干脆用函数指针, 这个是 Copy 的, 二次借用自然不存在
两者都否, 再考虑 Rc/Arc 吧
补充:
刚看到下面使用
Arc
的方案。安全性比裸指针好多了,性能也高。个人建议使用它。unsafe
还是能避免就避免好。首先恢复一下你的信心。rust的借用规则的选择是为了安全性牺牲了部分安全的使用场景。而你的方案涉及了多个借用问题,这个被借用规则拒绝也很正常。方法当然是有的,不过就看你选择是花你的时间还是机器时间了。
解决方法
方案1
高安全但是低性能。(花机器时间)
获取函数的时候,就不要使用引用了,改为使用所有权。我在学异步运行时实现时,看到的教程都是对
cx
用一次clone
一次。所以,你这里只是克隆函数又不上下文cx
,问题不大。方案2
高性能但是低安全。(花费大量自己时间)
使用裸指针,这样就没有了
cx
的二次借用问题。但是 安全自负!!! 你需要在每一处使用裸指针的地方保证指针有效性。https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c65470075352c10dd94083a0fe573fef
方案3
高性能+相对安全。(花微量自己时间)
该方案就是在方案2的基础上封装了一下。限制函数裸指针只会在一处地方使用。
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1f82fd96db17e8c2c20930ac589b09cf
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=05ea1ae4e247440add15c413091274bd
引用一段话 https://course.rs/advance/smart-pointer/cell-refcell.html
--
👇
祐祐: 我有试过,也许是我写法问题,但按理来说还是会造成同时可变借用,也就违背 RefCell 规则,导致运行时panic
--
👇
‘static: 单线程的话就看你能不能接受Rc<RefCell>>包起来了,map get 换成 map get+clone
我有试过,也许是我写法问题,但按理来说还是会造成同时可变借用,也就违背 RefCell 规则,导致运行时panic
--
👇
‘static: 单线程的话就看你能不能接受Rc<RefCell>>包起来了,map get 换成 map get+clone
单线程的话就看你能不能接受Rc<RefCell>>包起来了,map get 换成 map get+clone
不管怎么想好像似乎无解,真的不行的话可能要全部拆掉? (这很可怕...)...也让我对 rust 的灵活性感到有点失望。
很有趣,我看到这句 "用了在塞回去" 我整个笑喷。 回归正题。但是这样做性能会损耗太大,我设计解释器是想要让它处理数值密集型的操作。 先不讨论用了后再塞进去的性能消耗,HashMap 的 remove 看着也是很别扭。
--
👇
‘static: 用了在塞回去
--
👇
祐祐: 因为解释器里面的函数就像是标准程式库一样,所以需要反覆使用直到程式退出。
--
👇
‘static: + 如果不要求map里的函数复用的的话 可以修改成这样
用了在塞回去
--
👇
祐祐: 因为解释器里面的函数就像是标准程式库一样,所以需要反覆使用直到程式退出。
--
👇
‘static: + 如果不要求map里的函数复用的的话 可以修改成这样
因为解释器里面的函数就像是标准程式库一样,所以需要反覆使用直到程式退出。
--
👇
‘static: + 如果不要求map里的函数复用的的话 可以修改成这样