函数指针与 Fn 特征对象
function pointer vs Fn trait object
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(|x| x + 1, 5) // call
这个函数接受闭包和函数指针。它以函数指针作为参数类型。
什么时候我应该更喜欢这个而不是使用特征对象,比如 &dyn Fn(i32) -> i32
或 Box<dyn Fn(i32)-> i32>
而不是 fn
fn do_twice(f: &dyn Fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(&|x| x + 1, 5) // call
或
fn do_twice(f: Box<dyn Fn(i32) -> i32>, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
fn
类型是一个裸函数指针 (https://doc.rust-lang.org/std/primitive.fn.html)。
它不能与捕获环境的闭包一起使用,并且不能为您喜欢的类型手动实现(例如 impl Fn for MySuperType
)
因此,您的示例有效的唯一原因是它过于简单化了!
如果你把它弄得再复杂一点,它就会失败https://gist.github.com/rust-play/2167e73325daa1e2a179505209405917
When should I prefer this over using a trait object
Trait 对象不是唯一的其他选项。正如@DarthKotik 指出的那样,接受 fn
指针将不允许捕获其环境的闭包,但您可以只使用普通类型参数,以 Fn
为界来接受函数和闭包,而无需装箱任何东西:
fn do_twice<F>(f: F, arg: i32) -> i32
where
F: Fn(i32) -> i32
{
f(arg) + f(arg)
}
或者,等效地,但避免了额外的类型变量:
fn do_twice(f: impl Fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(|x| x + 1, 5) // call
这个函数接受闭包和函数指针。它以函数指针作为参数类型。
什么时候我应该更喜欢这个而不是使用特征对象,比如 &dyn Fn(i32) -> i32
或 Box<dyn Fn(i32)-> i32>
而不是 fn
fn do_twice(f: &dyn Fn(i32) -> i32, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
do_twice(&|x| x + 1, 5) // call
或
fn do_twice(f: Box<dyn Fn(i32) -> i32>, arg: i32) -> i32 { // definition
f(arg) + f(arg)
}
fn
类型是一个裸函数指针 (https://doc.rust-lang.org/std/primitive.fn.html)。
它不能与捕获环境的闭包一起使用,并且不能为您喜欢的类型手动实现(例如 impl Fn for MySuperType
)
因此,您的示例有效的唯一原因是它过于简单化了!
如果你把它弄得再复杂一点,它就会失败https://gist.github.com/rust-play/2167e73325daa1e2a179505209405917
When should I prefer this over using a trait object
Trait 对象不是唯一的其他选项。正如@DarthKotik 指出的那样,接受 fn
指针将不允许捕获其环境的闭包,但您可以只使用普通类型参数,以 Fn
为界来接受函数和闭包,而无需装箱任何东西:
fn do_twice<F>(f: F, arg: i32) -> i32
where
F: Fn(i32) -> i32
{
f(arg) + f(arg)
}
或者,等效地,但避免了额外的类型变量:
fn do_twice(f: impl Fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}