Reputation: 771
I'm confused by the following code (Listing 13-9):
struct Cacher<T>
where
T: Fn(i32) -> i32,
{
calculation: T,
value: Option<i32>,
}
I understand that Fn
is a trait, but usually a trait has no argument and returned type. How can I define a trait like Fn
?
I tried to look at the definition (actually it's FnOnce
, but Fn
has FnMut
bound and FnMut
has FnOnce
bound...), but I'm still confused. What is the meaning of that <Args>
? Then also something written about it in the Nomicon; but I do not understand it:
Where
Fn(a, b, c) -> d
is itself just sugar for the unstable realFn
trait
Upvotes: 5
Views: 885
Reputation: 430634
How can I define a trait with arguments and return types like Fn?
If you mean the syntax MyTrait(A) -> B
, you cannot. Traits with "arguments" and "return types" are special and are restricted to the Fn
, FnMut
and FnOnce
traits. This is hard-coded into the compiler. There's even a specific error message for it:
error: parenthetical notation is only stable when used with `Fn`-family traits (see issue #29625)
--> src/main.rs:5:8
|
5 | A: MyTrait(A) -> B,
| ^^^^^^^^^^^^^^^
That being said, this syntax desugars into the standard trait syntax. You can see what FnOnce
is from the docs:
pub trait FnOnce<Args> {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
The compiler converts Fn(A, B, C) -> Z
into Fn<(A, B, C), Output = Z>
. Args
is a standard trait generic type parameter and Output
is a standard associated type. The "rust-call"
ABI is some internal compiler machinery that makes this a bit more efficient and can be ignored most of the time.
You are completely allowed to create your own traits with generic parameters and associated types. You just are not allowed to use the parenthetical notation.
Upvotes: 9