在Rust中实现等效的多个可变(静态分配,静态分派等)回调的正确方法是什么? [英] What's the correct way to implement the equivalent of multiple mutable (statically allocated, statically dispatched, etc.) callbacks in Rust?
问题描述
我有以下示例代码,它是其他编程语言中事件驱动的API的标准基础,但是在Rust中,借位检查器会以一次不能多次借用p1
可变"来阻止它:
I have the following example code, which is the standard basis of event-driven APIs in other programming languages, but in Rust the borrow checker blocks it with "cannot borrow p1
as mutable more than once at a time":
struct Pen {
color_cmyk: u32,
ink: usize,
}
impl Pen {
pub fn new() -> Pen {
Pen {
color_cmyk: 0x80800000,
ink: 20000,
}
}
pub fn write(&mut self, text: &str) -> bool {
if self.ink < text.len() {
return false;
}
self.ink -= text.len();
true
}
}
fn main() {
println!("Hello, world !");
let mut p1 = Pen::new();
p1.write("Hello");
println!("ink: {}, color: {}", p1.ink, p1.color_cmyk);
let mut cb = |text| if p1.write(text) {
println!("{}", text);
} else {
println!("Out of ink !");
};
let mut cb2 = |text| {
p1.write(text);
p1.ink
};
cb("Hello");
cb("World");
println!("{}", cb2("Hello"));
}
error[E0499]: cannot borrow `p1` as mutable more than once at a time
--> src/main.rs:37:23
|
31 | let mut cb = |text| if p1.write(text) {
| ------ -- previous borrow occurs due to use of `p1` in closure
| |
| first mutable borrow occurs here
...
37 | let mut cb2 = |text| {
| ^^^^^^ second mutable borrow occurs here
38 | p1.write(text);
| -- borrow occurs due to use of `p1` in closure
...
45 | }
| - first borrow ends here
例如,该代码可用于实现对窗口的两个回调:一个用于处理键盘事件,另一个用于处理鼠标事件,二者均会更新窗口状态(例如:更改颜色,关闭窗口等).
The code can be used, for example, to implement two callbacks to a window: one for handling keyboard events and another for handling mouse events, both of which update the window state (ex: changing color, closing the window, etc.).
我知道这个问题会在Stack Overflow和其他论坛的其他地方出现,但总的来说,答案集中于描述问题的原因,很少提出完整的一般解决方案:
I know that this question appears elsewhere in Stack Overflow and other forums, but in general, the answers focus on describing the reason of the problem and rarely propose a complete general solution for it:
- Cannot borrow `x` as mutable more than once at a time
- How to bypass "cannot borrow as mutable more than once"?
- Cannot borrow as mutable more than once at a time
- Passing mutable context into callbacks
- Creating a callback system using closures
- Execute callbacks like as mutable borrowing from cycle
- Callback to mutable self
推荐答案
一种方法是使用RefCell
,它允许您仅使用&Pen
而不是&mut Pen
来进行变异,而以推动借入检查到运行时.非常便宜:没有分配,只有一个标志测试.
主要缺点是违反规则将导致运行时出现恐慌.一个有用的经验法则是永远不要借用超过必要的时间(认为它们是单线程互斥体").
One way is to use a RefCell
, which allows you to mutate things with only &Pen
instead of &mut Pen
, at the cost of pushing the borrow-checking to runtime. It’s very cheap: there is no allocation, just a single flag test.
The main downside is that violating the rules will result in a panic at runtime. A useful rule of thumb is to never borrow for any longer than necessary (think of them as "single-threaded mutexes").
use std::cell::RefCell;
fn main() {
println!("Hello, world !");
let p1 = RefCell::new(Pen::new());
{
let mut rp1 = p1.borrow_mut();
rp1.write("Hello");
println!("ink: {}, color: {}", rp1.ink, rp1.color_cmyk);
}
let cb = |text| {
if p1.borrow_mut().write(text) {
println!("{}", text);
}
else {
println!("Out of ink !");
}
};
let cb2 = |text| {
let mut rp1 = p1.borrow_mut();
rp1.write(text);
rp1.ink
};
cb("Hello");
cb("World");
println!("{}", cb2("Hello"));
}
另一种方法是设置回调系统,以将要修改的对象作为参数传入.要权衡的是,您的回调系统需要知道此状态.
Another way is to set up the callback system to pass in the object that you’re modifying as an argument. The trade-off is then your callback system needs to be aware of this state.
fn main() {
println!("Hello, world !");
let mut p1 = Pen::new();
p1.write("Hello");
println!("ink: {}, color: {}", p1.ink, p1.color_cmyk);
let cb = |p1: &mut Pen, text| if p1.write(text) {
println!("{}", text);
} else {
println!("Out of ink !");
};
let cb2 = |p1: &mut Pen, text| {
p1.write(text);
p1.ink
};
cb(&mut p1, "Hello");
cb(&mut p1, "World");
println!("{}", cb2(&mut p1, "Hello"));
}
这篇关于在Rust中实现等效的多个可变(静态分配,静态分派等)回调的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!