我正在尝试在特征对象上使用回调函数.我将我的问题减少到以下代码(围栏):
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)
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代码一样。