【问题标题】:closure implementing FnMut not working on place where dyn FnMut is expected实现 FnMut 的关闭不能在预期 dyn FnMut 的地方工作
【发布时间】:2019-12-22 22:05:27
【问题描述】:
use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;

fn main() {
    let closure_cell = Rc::new(RefCell::new(None));
    let closure_cell_clone = Rc::clone(&closure_cell);
    *closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
    })));
}
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
}

在上面的代码中 request_animation_frame 期望 &amp;Closure&lt;dyn FnMut()&gt; 并且我传递了一个实现 FnMnutclosure。但我收到一个错误

expected type `std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>`
found type `std::option::Option<wasm_bindgen::closure::Closure<[closure@src/lib.rs:29:62: 39:6 bucket:_, world:_, closure_cell_clone:_]>>`

如果闭包可以实现该特征,则默认实现FnMut?不能发送实现 FnMut 的闭包来代替 dyn FnMut 吗?我在这里错过了什么?

【问题讨论】:

标签: rust


【解决方案1】:

这实际上是一个非常隐蔽的问题。原因如下:

虽然您可以将Box&lt;T&gt; 强制转换为Box&lt;dyn Trait&gt;(如果T: Trait),但这两个是不兼容的类型,因为您不能将Box&lt;T&gt; 类型的变量或字段用作Box&lt;dyn Trait&gt;

这是一个示例,说明为什么这会导致未定义的行为,使用 Cell&lt;...&gt;s(该框将是 Cell&lt;...&gt; 中的某个字段)

use std::cell::Cell;

trait SomeTrait { }

#[derive(Clone, Copy)]
pub struct A;

impl SomeTrait for A { }

#[derive(Clone, Copy)]
pub struct B;

impl SomeTrait for B { }

fn use_coerced(cell: &Cell<Box<dyn SomeTrait>>) {
    cell.set(Box::new(B));  // valid, since `B` implements `SomeTrait`
}

fn main() {
    // I'll put what the type inference will come up with as explicit types here
    let some_cell: Cell<Box<A>> = Cell::new(Box::new(A));
    use_coerced(&some_cell);  // If cells would coerce, this would be valid
    let value: Box<A> = some_cell.into_inner();  // But we put `Box<B>` in there. So we'd have undefined behaviour.
}

所以要解决这个问题,你必须告诉编译器在某处使用dyn FnMut() 类型而不是闭包类型。我个人更喜欢在创建 Box 后明确地取消其大小:

fn main() {
    let closure_cell = Rc::new(RefCell::new(None));
    let closure_cell_clone = Rc::clone(&closure_cell);
    *closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
    }) as Box<dyn FnMut()>));
}

fn request_animation_frame(f: &Closure<dyn FnMut()>) { }

不过,您也可以指定单元格的类型:

fn main() {
    let closure_cell: Rc<RefCell<Option<Closure<dyn FnMut()>>>> = Rc::new(RefCell::new(None));
    let closure_cell_clone = Rc::clone(&closure_cell);
    *closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
    })));
}

fn request_animation_frame(f: &Closure<dyn FnMut()>) { }

但是,正如您所见,它更加冗长,而且是不必要的。

【讨论】:

  • 我仍然不明白为什么强制没有发生以及为什么每个闭包都是不同的类型很重要。如果我是对的,闭包会隐式实现FnMut。所以强制不应该发生不应该吗?另一个没有闭包的例子,这里没有as play.rust-lang.org/…
  • @raj 我已将答案更新为更多解释,而且只是更好
  • 但是如果 funcion 签名是 fn use_coerced(cell: Cell&lt;Box&lt;dyn SomeTrait&gt;&gt;) { ,就会发生强制。如果函数接受 Cell 而不是引用,它将强制执行。
  • @raj 是的,因为在这种情况下它是安全的,因为无论如何您都无法取回原始类型。但老实说,这感觉有点离题,因为那离你原来的问题只有几英里远。答案是“帮助 Rusts 类型推断”。
猜你喜欢
  • 2015-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多