我正在尝试在Rust中的顶级函数中使用特征。
trait FnTrait {
fn call(self);
}
impl FnTrait for fn() {
fn call(self) {
self()
}
}
fn foo() {
println!("Hello, World!")
}
fn main() {
FnTrait::call(foo)
}
但是,下面的代码无法使用(Playground Link)
进行编译error[E0277]: the trait bound `fn() {foo}: FnTrait` is not satisfied
--> <anon>:16:5
|
16 | FnTrait::call(foo)
| ^^^^^^^^^^^^^ the trait `FnTrait` is not implemented for `fn() {foo}`
|
= help: the following implementations were found:
<fn() as FnTrait>
= note: required by `FnTrait::call`
我发现我可以通过像{<}>那样投射foo
来欺骗它进行编译
FnTrait::call(foo as fn())
但令人讨厌,我的程序中的一些功能比foo
更复杂。有什么方法可以避免演员表演?我的特质是否有问题?
答案 0 :(得分:3)
Rust中的每个函数都有自己的类型。如您所见,foo
不是fn()
,而是fn() {foo}
;遗憾的是,这不是您可以在源代码中编写的实际类型,这只是编译器消息的事情。存在这样的区别,使编译器更容易让您将函数作为值传递,同时仍然能够内联调用。
结果是命名函数指针在没有强制转换或类型提示的情况下不能转换为通用函数指针。例如,这有效:
fn foo() {
println!("Hello, World!")
}
fn bar(f: fn()) {
f()
}
fn main() {
bar(foo)
}
但是,我不知道有任何方法可以利用它来使特性发挥作用。
前进的唯一方法是停止尝试实现函数指针的特征,而是为可调用的所有内容实现它:
trait FnTrait {
fn call(self);
}
impl<F> FnTrait for F where F: FnOnce() {
fn call(self) {
self()
}
}
fn foo() {
println!("Hello, World!")
}
fn main() {
foo.call();
}
(关于the difference between Fn
, FnMut
and FnOnce
的半相关答案。)
这适用于可以使用该签名调用的任何内容,包括函数和闭包。缺点是你只能有一个这样的实现。您无法为任何其他签名实现此特征。
一个通用实现,或许多特定实现和大量手动转换。选择你的毒药。
顺便说一下:在Rust中没有“顶级函数”这样的东西,至少不是与其他类型函数不同的东西。功能是功能,无论它们出现在何处。实例函数 a.k.a。方法仍然是常规函数,只是它们的第一个参数被称为“self”。