在特征对象上使用回调

Fer*_*rio 5 rust

我正在尝试在特征对象上使用回调函数.我将我的问题减少到以下代码(围栏):

trait Caller {
    fn call(&self, call: fn(&Caller)) where Self: Sized {
        call(self)
    }
}

struct Type;
impl Caller for Type {}

fn callme(_: &Caller) {}

fn main() {
    let caller: Box<Caller> = Box::new(Type);
    caller.call(callme); // does not work
    //callme(&*caller);  // works
}
Run Code Online (Sandbox Code Playgroud)

结果

<anon>:14:12: 14:24 error: the trait `core::marker::Sized` is not implemented for the type `Caller` [E0277]
<anon>:14     caller.call(callme); // does not work
                     ^~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

添加Sized绑定到Caller结果:

<anon>:3:14: 3:18 error: cannot convert to a trait object because trait `Caller` is not object-safe [E0038]
Run Code Online (Sandbox Code Playgroud)

我真的不明白为什么我需要Sized特质的约束.有趣的是,如果我直接使用回调它.我如何让它工作?

编辑:由于答案,我现在提出了一个很好的解决方案

trait Caller {
    fn borrow(&self) -> &Caller;
    fn call(&self, call: fn(&Caller)) {
        call(self.borrow())
    }
}

struct Type;
impl Caller for Type {
    fn borrow(&self) -> &Caller { self }
}

fn callme(_: &Caller) {}

fn main() {
    let caller: Box<Caller> = Box::new(Type);
    caller.call(callme);
}
Run Code Online (Sandbox Code Playgroud)

huo*_*uon 3

call中的参数fn call采用特征对象&Caller,因此调用它需要强制self(类型&Self)对&Caller特征对象的引用。仅当&Self是瘦指针而不是像特征对象或&[T]切片这样的胖指针时,才可以进行强制转换。&Self正是当 时 是一个细指针Self: Sized。编译器默认Selfin Traits not being Sized,因此需要额外的限制。该Sized特征表示该类型具有在编译时已知的大小,无需存储额外的信息(在指针旁边,使其“胖”)来在运行时计算它。

不幸的是,这留下了一个漏洞:AFAIK,实际上不可能让这样的方法成为默认方法,并且仍然能够在特征对象上调用它,因为特征对象&Caller具有Self = Callerwhich isn't Sized。但是,如果为每种类型手动实现该方法,它应该可以工作:

trait Caller {
    fn call(&self, call: fn(&Caller));
}

struct Type;
impl Caller for Type {
    fn call(&self, call: fn(&Caller)) {
        call(self)
    }
}

fn callme(_: &Caller) {}

fn main() {
    let caller: Box<Caller> = Box::new(Type);
    caller.call(callme);
}
Run Code Online (Sandbox Code Playgroud)

特征中的方法call声明不再需要 ,where Self: Sized因为它本身并不尝试对特征对象进行强制转换,并且具体实现对如何&Caller获取特征对象有更多的控制。对于Sized类型,它直接工作,就像原始where Self: Sized代码一样。