我正试图在Rust 1.11中实现一个回调系统。我想我已经设置好了存储回调的系统,但我在实际调用它们时遇到了问题。示例代码:
struct Container<'a, T> {
callbacks: Vec<Box<FnMut(T) + 'a>>,
}
impl<'a, T: Copy + PartialEq> Container<'a, T> {
fn new() -> Self {
Container {
callbacks: Vec::new(),
}
}
fn add_callback<F: 'a + FnMut(T)>(&mut self, callback: F) -> usize {
let cb_id = self.callbacks.len();
self.callbacks.push(Box::new(callback));
cb_id
}
fn call_by_id(&self, cb_id: usize, value: T) {
// This doesn't work and I don't know why:
self.callbacks[cb_id](value);
// It still doesn't work (same error) when I try to dereference the Box
// *self.callbacks[cb_id](value);
// It's not a dereferencing scoping issue, either
// *(self.callbacks[cb_id])(value);
}
}
fn main() {
let mut list = Vec::new();
{
let mut container = Container::new();
let append = container.add_callback(|v| list.push(v));
container.call_by_id(append, 3);
container.call_by_id(append, 4);
}
println!("List contains: {:?}", list);
// Expect "List contains: [3, 4]", but it doesn't compile
}
(游乐场链接)
这会产生以下错误:
error: expected function, found `Box<std::ops::FnMut(T) + 'a>`
--> <anon>:20:9
|>
20 |> self.callbacks[cb_id](value);
|> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
对于Rust来说,这是异常无益的;我真的很难通过谷歌或在这里搜索找到这个错误的其他例子。
Box
文档声称有一个通用的impl<T> Deref for Box<T> where T: ?Sized
,而Sized
文档则表示语法?Sized
用于删除对Sized
特性的要求。我理解这意味着,Box
为每个T
实现Deref
,无论大小,所以它应该在这里发挥作用。
如果是这样的话,我根本不明白为什么我不能直接调用包含回调的Box
,或者(最坏的情况)取消引用self.callbacks[cb_id]
来访问可调用的。我目前最好的猜测是,这与游戏中的一生有关,但如果是这样,我只是不知道如何调整它们以使其发挥作用。
这里有一个较小的复制:
fn main() {
let a: Vec<Box<FnMut()>> = vec![Box::new(|| println!("called"))];
a[0]();
}
让我们将其分解并观察类型和错误:
fn call_by_id(&self, cb_id: usize, value: T) {
let () = self.callbacks[cb_id];
}
这表明该类型为Box<std::ops::FnMut(T)>
。到目前为止,一切都很好。将其存储在变量中并继续:
fn call_by_id(&self, cb_id: usize, value: T) {
let x = self.callbacks[cb_id];
x(value);
}
啊,对:error: cannot borrow immutable `Box` content `*x` as mutable
。。。
fn call_by_id(&self, cb_id: usize, value: T) {
let mut x = self.callbacks[cb_id];
x(value);
}
Oops:error: cannot move out of indexed content
。。。
fn call_by_id(&self, cb_id: usize, value: T) {
let mut x = &mut self.callbacks[cb_id];
x(value);
}
啊哈:error: cannot borrow immutable field `self.callbacks` as mutable
。。。
fn call_by_id(&mut self, cb_id: usize, value: T) {
let mut x = &mut self.callbacks[cb_id];
x(value);
}
它编译了我可能会把它留在这里(x
的名字更好),但让我们看看是否可以把它放回一行。直接替代:
fn call_by_id(&mut self, cb_id: usize, value: T) {
(&mut self.callbacks[cb_id])(value);
}
不,回到error: expected function, found `&mut Box<std::ops::FnMut(T) + 'a>`
也许是一个取消引用:
fn call_by_id(&mut self, cb_id: usize, value: T) {
(*self.callbacks[cb_id])(value);
}
没有,error: cannot borrow immutable
信箱content as mutable
。更具体地说可变性:
fn call_by_id(&mut self, cb_id: usize, value: T) {
(*&mut self.callbacks[cb_id])(value);
}
这很有效,但我不确定它是否优雅。
总之,问题是回调的变量是不可变的。这是由两件事引起的:
- 回调向量的结合是不可变的(通过
&self
) - 回调的取消引用似乎不理解可变性要求,除非您是明确的。我不能百分之百确定为什么会这样
请注意,在您的注释代码中:
*self.callbacks[cb_id](value);
*(self.callbacks[cb_id])(value);
我确信这些都是一样的;*
的优先级将把它绑定到整个值的结果。我想你的意思是:
(*self.callbacks[cb_id])(value);