I'm getting confused by this.
If the following works:
fn func_exit() -> bool {
println!("hi");
true
}
fn locate_func() -> fn() -> bool {
func_exit
}
Why these following syntaxes throw error?
fn locate_func1<F: Fn() -> bool>() -> F {
func_exit
}
fn locate_func2<F>() -> F where F:Fn() -> bool {
func_exit
}
I'm new to Rust and the following error message is not clear to me:
error[E0308]: mismatched types
--> src/main.rs:44:9
|
43 | fn locate_func1<F: Fn() -> bool>() -> F {
| - this type parameter - expected `F` because of return type
44 | func_exit
| ^^^^^^^^^ expected type parameter `F`, found fn item
|
= note: expected type parameter `F`
found fn item `fn() -> bool {func_exit}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
error[E0308]: mismatched types
--> src/main.rs:47:9
|
46 | fn locate_func2<F>() -> F where F:Fn() -> bool {
| - - expected `F` because of return type
| |
| this type parameter
47 | func_exit
| ^^^^^^^^^ expected type parameter `F`, found fn item
|
= note: expected type parameter `F`
found fn item `fn() -> bool {func_exit}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
Appreciate some help.
Thanks
fn locate_func() -> fn() -> bool {
func_exit
}
locate_func returns a pointer to a function returning bool (i.e. fn() -> bool).
fn locate_func1<F: Fn() -> bool>() -> F {
func_exit
}
locate_func1 means that the caller can specify any F satisfying the bounds Fn() -> bool, and it will return an F. But it is clearly not the case that func_exit is necessarily of the type specified by the caller.
locate_func2 has the same problem, just with where-Notation.
What you probably want could be the following:
fn locate_func3() -> impl Fn() -> bool {
func_exit
}
It says: locate_func3 returns something that implements the bound Fn() -> bool (and func_exit does so, so it can be returned there).
Also note the difference between fn (function pointers) and Fn ("something callable"):
The error is weird, but... you can't just return a bounded value like this, because bounded types are decided by the caller, and it doesn't make sense for the caller to decide what the return type is unless that return type is a consequence of an input type (e.g. an identity function is a trivial example of this). Which is not the case here.
If you want to return something generic, you need either some sort of trait object (so e.g. box the function and return a Box) or an impl (which still has a concrete return value but hides it at the API level).
The latter is more efficient but only allows returning a single concrete type, the former is less efficient but lets you return e.g. different closures.
Related
I have the following code (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
To me, it seems like this should compile as foo and bar have the same signature: fn(u32) -> bool. Yet, I get the following error:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
The same error can be triggered with this code:
let mut x = foo;
x = bar; // <-- error
I also tried to cast bar to the function pointer type again:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
This resulted in a slightly different error:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
I don't understand these errors at all. What are fn items vs. fn pointers and why are foo and bar different fn items?
Function item vs. function pointer
When you refer to a function by its name, the type you get is not a function pointer (e.g. fn(u32) -> bool). Instead, you get an a zero-sized value of the function's item type (e.g. fn(u32) -> bool {foo}).
The value is zero-sized because it doesn't store the actual function pointer. The type perfectly identifies the function, so there is no need to store actual data in the type. This has several advantages, mostly about easier optimizations. A function pointer is like you would expect from other languages: it stores an address to the function.
A function pointer refers to the function via stored address; a function item refers to the function via type information.
A function item can be coerced to a function pointer in many situations, for example: as argument to a function and in let _: fn(u32) -> bool = foo; statements. Additionally, you can explicitly cast a function item to a function pointer: foo as fn(u32) -> bool.
You can read more about this topic in the reference on function items, function pointers and coercion.
Solution to your problem
In your case, the compiler isn't smart enough to figure out that you want the function pointers from your foo and bar instead of function items. When you call call_both(foo, bar) the compiler sets the generic type F to be fn(u32) -> bool {foo}, because that's the type of the first argument. And then it complains that the second argument doesn't have the same type.
You can fix that by explicitly specifying the F parameter:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
After specifying the type, the compiler can correctly coerce the arguments to a function pointer. You could also as-cast the first argument to fn(u32) -> bool explicitly.
You can fix your second example by explicitly stating the function pointer type, too:
let mut x: fn(u32) -> bool = foo;
x = bar;
In general: specifying the function pointer type somewhere to trigger the coercion will work.
Each named function has a distinct type since Rust PR #19891 was merged. You can, however, cast the functions to the corresponding function pointer type with the as operator.
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
It's also valid to cast only the first of the functions: the cast will be inferred on the second, because both functions must have the same type.
call_both(foo as fn(u32) -> bool, bar);
Recently, I tried to write a piece of code similar to the following:
pub struct Foo<'a, F> /* where F: Fn(&u32) -> bool */ {
u: &'a u32,
f: F
}
impl<'a, F> Foo<'a, F>
where F: Fn(&u32) -> bool
{
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
where G: Fn(&u32) -> bool
{
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
}
}
Here, an instance of Foo represents a condition on a piece of data (the u32), where a more restrictive Foo can be built from a less restrictive one via new_foo, without consuming the old. However, the above code does not compile as written, but gives the rather cryptic error message:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
error: higher-ranked subtype error
--> src/lib.rs:9:5
|
9 | / pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
10 | | where G: Fn(&u32) -> bool
11 | | {
12 | | Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
13 | | }
| |_____^
error: aborting due to 2 previous errors
After much experimentation, I did find a way to make the code compile, and I believe it then functions as intended. I am used to the convention of placing bounds on impls rather than declarations when the declaration can be written without relying on those bounds, but for some reason uncommenting the where clause above, that is, copying the bound F: Fn(&u32) -> bool from the impl to the declaration of Foo itself resolved the problem. However, I don't have a clue why this makes a difference (nor do I really understand the error message in the first place). Does anyone have an explanation of what's going on here?
The only subtypes that exist in Rust are lifetimes, so your errors (cryptically) hint that there's some sort of lifetime problem at play. Furthermore, the error clearly points at the signature of your closure, which involves two lifetimes:
the lifetime of the closure itself, which you have explicitly stated outlives the anonymous lifetime '_; and
the lifetime of its argument &u32, which you have not explicitly stated, so a higher-ranked lifetime is inferred as if you had stated the following:
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
where G: Fn(&u32) -> bool
Using the more explicit signature above gives a (very) slightly more helpful error:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&'b u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
At least we can now see that "one type is more general than the other": we expected a closure that can take an argument with any lifetime but for some reason Rust thinks that what we have instead is a closure that takes an argument that may have some more restricted range of lifetimes.
What's happened? Well, the function's return value is the following expression:
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
This is of course an instance of struct Foo<'a, F>, where this F bears no relation to that declared on the impl block (with its trait bound). Indeed, since there's no explicit bound on F in the struct definition, the compiler must fully infer this type F from the expression itself. By giving the struct definition a trait bound, you are telling the compiler that instances of Foo, including the above expression, have an F that implements for<'b> Fn(&'b u32) -> bool: i.e. the range of lifetimes for the &u32 argument are unbounded.
Okay, so the compiler needs to infer F instead, and indeed it does infer that it implements Fn(&u32) -> bool. However, it's just not quite smart enough to determine to what range of lifetimes that &u32 argument might be restricted. Adding an explicit type annotation, as suggested in #rodrigo's comment above, states that the argument can indeed have any lifetime.
If there are in fact some restrictions on the possible lifetimes of the closure's argument, you would need to indicate that more explicitly by changing the definition of 'b from a higher-ranked trait bound (i.e. for<'b> in the return type above) to whatever is appropriate for your situation.
Hopefully once chalk is fully integrated into the compiler it will be able to perform this inference in both the unrestricted and restricted cases. In the meantime, the compiler is erring on the side of caution and not making potentially erroneous assumptions. The errors could definitely have been a bit more helpful, though!
function A which take a function B as parameter, again the function B take function C as parameter. I try the syntax like below, but this gives me an error:
fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () {
// ...
}
error[E0666]: nested `impl Trait` is not allowed
--> src/main.rs:2:21
|
2 | fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () {
| --------^^^^^^^^^^^^^^^-------
| | |
| | nested `impl Trait` here
| outer `impl Trait`
For some reason, I can't use &dyn keyword:
fn a(b: impl Fn(&dyn Fn() -> ()) -> ()) -> () {
// ...
}
Are there another ways to do this?
And I don't know why nested impl Trait cause an error.
Nested impl Trait doesn't work because it hasn't been specified or implemented yet. Also, because it isn't very useful given how other parts of Rust work.
fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () can be written using full generic syntax as
fn a<B, C>(b: B) -> ()
where B: Fn(C) -> (),
C: Fn() -> ()
So what does that mean? It means that B is something callable that takes an argument of something else callable. But the important thing here is in the concrete types. Specifically, B doesn't take any callable with a compatible signature, but specifically a C. What is C? Well, that's the issue. If you call a like this:
a(|f| f());
then C is the type of the lambda's parameter f, but that type is not known, since the parameter f's type cannot be deduced from usage alone. But suppose that wasn't a problem, what would the body of a look like?
fn a<B, C>(b: B) -> ()
where B: Fn(C) -> (),
C: Fn() -> () {
b(|| ())
}
Here we attempt to call b passing lambda. But the lambda's type is unnamed local lambda type, not C. Since C was passed in from the outside, it cannot be the type of a lambda local to a. Simply put, unless you pass in a C as an additional parameter to a, there is nothing that a has that it could pass to b.
What you apparently want is for B to be not a function object that can be called with some C, but with any C. You want it to be a polymorphic function object. Rust doesn't support compile-time polymorphic function objects (the equivalent in Haskell would be a forall a. a -> IO () or similar). It only supports runtime polymorphic function objects.
That's the job of dyn. Now you've said you can't use &dyn, because you want to pass the function object to another thread. So instead, use Box<dyn>:
fn a(b: impl Fn(Box<dyn Fn() -> ()>) -> ()) -> () {
b(Box::new(|| println!("Hello")))
}
fn main() {
a(move |f| { f(); f(); });
}
I have the following code (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
To me, it seems like this should compile as foo and bar have the same signature: fn(u32) -> bool. Yet, I get the following error:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
The same error can be triggered with this code:
let mut x = foo;
x = bar; // <-- error
I also tried to cast bar to the function pointer type again:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
This resulted in a slightly different error:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
I don't understand these errors at all. What are fn items vs. fn pointers and why are foo and bar different fn items?
Function item vs. function pointer
When you refer to a function by its name, the type you get is not a function pointer (e.g. fn(u32) -> bool). Instead, you get an a zero-sized value of the function's item type (e.g. fn(u32) -> bool {foo}).
The value is zero-sized because it doesn't store the actual function pointer. The type perfectly identifies the function, so there is no need to store actual data in the type. This has several advantages, mostly about easier optimizations. A function pointer is like you would expect from other languages: it stores an address to the function.
A function pointer refers to the function via stored address; a function item refers to the function via type information.
A function item can be coerced to a function pointer in many situations, for example: as argument to a function and in let _: fn(u32) -> bool = foo; statements. Additionally, you can explicitly cast a function item to a function pointer: foo as fn(u32) -> bool.
You can read more about this topic in the reference on function items, function pointers and coercion.
Solution to your problem
In your case, the compiler isn't smart enough to figure out that you want the function pointers from your foo and bar instead of function items. When you call call_both(foo, bar) the compiler sets the generic type F to be fn(u32) -> bool {foo}, because that's the type of the first argument. And then it complains that the second argument doesn't have the same type.
You can fix that by explicitly specifying the F parameter:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
After specifying the type, the compiler can correctly coerce the arguments to a function pointer. You could also as-cast the first argument to fn(u32) -> bool explicitly.
You can fix your second example by explicitly stating the function pointer type, too:
let mut x: fn(u32) -> bool = foo;
x = bar;
In general: specifying the function pointer type somewhere to trigger the coercion will work.
Each named function has a distinct type since Rust PR #19891 was merged. You can, however, cast the functions to the corresponding function pointer type with the as operator.
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
It's also valid to cast only the first of the functions: the cast will be inferred on the second, because both functions must have the same type.
call_both(foo as fn(u32) -> bool, bar);
I have the following code (Playground):
// Two dummy functions, both with the signature `fn(u32) -> bool`
fn foo(x: u32) -> bool {
x % 2 == 0
}
fn bar(x: u32) -> bool {
x == 27
}
fn call_both<F>(a: F, b: F)
where
F: Fn(u32) -> bool,
{
a(5);
b(5);
}
fn main() {
call_both(foo, bar); // <-- error
}
To me, it seems like this should compile as foo and bar have the same signature: fn(u32) -> bool. Yet, I get the following error:
error[E0308]: mismatched types
--> src/main.rs:20:20
|
20 | call_both(foo, bar);
| ^^^ expected fn item, found a different fn item
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool {bar}`
The same error can be triggered with this code:
let mut x = foo;
x = bar; // <-- error
I also tried to cast bar to the function pointer type again:
let mut x = foo;
x = bar as fn(u32) -> bool; // <-- error
This resulted in a slightly different error:
error[E0308]: mismatched types
--> src/main.rs:20:9
|
20 | x = bar as fn(u32) -> bool;
| ^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected type `fn(u32) -> bool {foo}`
found type `fn(u32) -> bool`
I don't understand these errors at all. What are fn items vs. fn pointers and why are foo and bar different fn items?
Function item vs. function pointer
When you refer to a function by its name, the type you get is not a function pointer (e.g. fn(u32) -> bool). Instead, you get an a zero-sized value of the function's item type (e.g. fn(u32) -> bool {foo}).
The value is zero-sized because it doesn't store the actual function pointer. The type perfectly identifies the function, so there is no need to store actual data in the type. This has several advantages, mostly about easier optimizations. A function pointer is like you would expect from other languages: it stores an address to the function.
A function pointer refers to the function via stored address; a function item refers to the function via type information.
A function item can be coerced to a function pointer in many situations, for example: as argument to a function and in let _: fn(u32) -> bool = foo; statements. Additionally, you can explicitly cast a function item to a function pointer: foo as fn(u32) -> bool.
You can read more about this topic in the reference on function items, function pointers and coercion.
Solution to your problem
In your case, the compiler isn't smart enough to figure out that you want the function pointers from your foo and bar instead of function items. When you call call_both(foo, bar) the compiler sets the generic type F to be fn(u32) -> bool {foo}, because that's the type of the first argument. And then it complains that the second argument doesn't have the same type.
You can fix that by explicitly specifying the F parameter:
call_both::<fn(u32) -> bool>(foo, bar);
call_both::<fn(_) -> _>(foo, bar); // <-- even this works
After specifying the type, the compiler can correctly coerce the arguments to a function pointer. You could also as-cast the first argument to fn(u32) -> bool explicitly.
You can fix your second example by explicitly stating the function pointer type, too:
let mut x: fn(u32) -> bool = foo;
x = bar;
In general: specifying the function pointer type somewhere to trigger the coercion will work.
Each named function has a distinct type since Rust PR #19891 was merged. You can, however, cast the functions to the corresponding function pointer type with the as operator.
call_both(foo as fn(u32) -> bool, bar as fn(u32) -> bool);
It's also valid to cast only the first of the functions: the cast will be inferred on the second, because both functions must have the same type.
call_both(foo as fn(u32) -> bool, bar);