在rust中有2种方法来传入函数指针,一种通过 fn
另外一种是通过Fn FnMut FnOnce
三兄弟
fn call_fn_pointer(f: fn(&str) -> String) {
let res = f("fn pointer");
println!("{}", res);
}
fn main() {
call_fn_pointer(|s| { s.to_string() });
}
或者
fn call_fn_trait<T: Fn(&str) -> String>(f: T) {
let res = f("fn trait");
println!("{}", res);
}
fn main() {
call_fn_trait(|s| { s.to_string() });
}
这两种方式都可以当做函数指针,区别是什么呢?
Function pointers are pointers that point to code, not data. They can be called just like functions. Like references, function pointers are, among other things, assumed to not be null, so if you want to pass a function pointer over FFI and be able to accommodate null pointers, make your type Option
with your required signature Plain function pointers are obtained by casting either plain functions, or closures that don’t capture an environment:
let outer = String::from("hello");
call_fn_pointer(|s| {
println!("{}", outer.len());
s.to_string()
});
call_fn_trait(|s| {
println!("{}", outer.len());
s.to_string()
});
error[E0308]: mismatched types
--> src/main.rs:10:21
|
10 | call_fn_pointer(|s| {
| _____________________^
11 | | println!("{}", outer.len());
12 | | s.to_string()
13 | | });
| |_____^ expected fn pointer, found closure
|
= note: expected fn pointer `for<'r> fn(&'r str) -> String`
found closure `[closure@src/main.rs:10:21: 13:6]`
note: closures can only be coerced to `fn` types if they do not capture any variables
可以看到,如果fn capture了 outter variable, 那么这里就不能用fn pointer了, 必须用Fn三兄弟。
下面的例子是我碰到的一个比较奇怪的例子,目前还不知道具体原因,但被编译器教育过后知道了 fn 大概率不能用于实现 trait
trait Foo {
fn call(&mut self, msg: &str) -> String;
}
impl Foo for fn(&str) -> String {
fn call(&mut self, msg: &str) -> String {
(*self)(msg)
}
}
fn with_foo<F: Foo>(mut foo: F) -> Option<String> {
let res = foo.call("hello");
Some(res)
}
// so far so good
fn main() {
// 但当我调用with_foo时,编译报错
with_foo(|s: &str| { s.to_string()});
}
error[E0277]: the trait bound `[closure@src/main.rs:11:14: 11:40]: Foo` is not satisfied
--> src/main.rs:11:5
|
11 | with_foo(|s: &str| { s.to_string()});
| ^^^^^^^^ the trait `Foo` is not implemented for `[closure@src/main.rs:11:14: 11:40]`
|
note: required by a bound in `with_foo`
--> src/main.rs:44:16
|
44 | fn with_foo<F: Foo>(mut foo: F) -> Option<String> {
| ^^^ required by this bound in `with_foo`
难道不能用clousure?那我改成函数看看
fn foo(s: &str) -> String {
s.to_string()
}
with_foo(foo);
error[E0277]: the trait bound `for<'r> fn(&'r str) -> String {foo}: Foo` is not satisfied
--> src/main.rs:14:14
|
14 | with_foo(foo);
| -------- ^^^ the trait `Foo` is not implemented for `for<'r> fn(&'r str) -> String {foo}`
| |
| required by a bound introduced by this call
|
任然不行。而且报错信息也没有给出明确的问题,换成Fn trait试试
impl<F> Foo for F where F: Fn(&str) -> String {
fn call(&mut self, msg: &str) -> String {
(*self)(msg)
}
}
with_foo(|s: &str| {s.to_string()});
竟然编译通过了…这里实际上我不太能理解,为什么用fn不可以,而用Fn 是可以编译通过的? 难道trait只能应用在 Fn trait上?但为什么又允许为 fn 实现trait呢?
error[E0119]: conflicting implementations of trait `Foo` for type `for<'r> fn(&'r str) -> std::string::String`
--> src/main.rs:36:1
|
30 | impl Foo for fn(&str) -> String {
| ------------------------------- first implementation here
...
36 | impl<F> Foo for F where F: Fn(&str) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(&'r str) -> std::string::String`
这里还发现个有意思的问题, 这两个impl 不能同时存在, 编译器认为这两个一致?这里让我有些困惑