Reputation: 417
#![feature(unboxed_closures)]
#![feature(fn_traits)]
struct foo;
impl std::ops::Add for foo {
type Output = foo;
fn add(self, x: foo) -> foo {
println!("Add for foo");
x
}
}
impl Fn for foo {
extern "rust-call" fn call(&self) -> Self {
println!("Call for Foo ");
self
}
}
fn main() {
let x = foo;
let y = foo;
x + y;
x();
}
I implemented the Add
trait, but I don't understand how to call the struct as a function. I get the error:
error[E0243]: wrong number of type arguments: expected 1, found 0
--> src/main.rs:14:10
|
14 | impl Fn for foo {
| ^^ expected 1 type argument
I'm new to Rust, and can't find examples how to make this thing happen.
Upvotes: 27
Views: 11045
Reputation: 5553
By implementing Deref, you can simulate the desired behavior. Then you just use parentheses around parameters and get a return value just like you were calling a function.
See this thread: https://users.rust-lang.org/t/callable-struct-on-stable/54689/6
The relevant part (thanks to crawdad):
fn main() {
let func: Functionish = make_func();
func();
}
fn make_func() -> Functionish {
Functionish {
f: Box::new(|| println!("printing")),
}
}
struct Functionish {
f: Box<dyn Fn()>,
}
impl std::ops::Deref for Functionish {
type Target = dyn Fn();
fn deref(&self) -> &Self::Target {
&self.f
}
}
Here is an incomplete example from my own code that takes parameters, one a ref and one a mutable ref. In my case, the closure is stored inside an Rc, which necessitated the odd line &(*self.function_container)
because I need to go into the Rc before I can get the reference to the closure.
pub struct FeelFunction {
pub function_type: FunctionType,
name_container: RefCell<String>,
function_container: Rc<dyn Fn(&FeelValue, &mut NestedContext) -> FeelValue>
}
impl Deref for FeelFunction {
type Target = dyn Fn(&FeelValue, &mut NestedContext) -> FeelValue;
fn deref(&self) -> &Self::Target {
&(*self.function_container)
}
}
Upvotes: 9
Reputation: 430320
You cannot yet implement the Fn*
traits in stable Rust. This is only possible with the nightly compiler using #[feature]
!
It's very useful to fully read the trait that you are implementing to see how to implement it. The Fn
trait is defined as:
pub trait Fn<Args>: FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
Notice any differences between the implementation and the definition? I see many:
The implementation doesn't provide a value for Args
! That's what the compiler is pointing at. See also Wrong number of type arguments: expected 1 but found 0
The implementation doesn't implement the supertrait FnMut
, which itself requires the supertrait FnOnce
. FnOnce
is where the associated type Output
is declared.
The implementation neglects to define what concrete type Output
should be.
The implementation returns Self
while the trait returns Self::Output
.
The implementation doesn't accept the second argument to call
. This argument contains any arguments passed in.
Additionally, types in Rust use PascalCase
, not snake_case
, so it should be Foo
.
#![feature(unboxed_closures)]
#![feature(fn_traits)]
struct Foo;
impl Fn<()> for Foo {
extern "rust-call" fn call(&self, _args: ()) {
println!("Call (Fn) for Foo");
}
}
impl FnMut<()> for Foo {
extern "rust-call" fn call_mut(&mut self, _args: ()) {
println!("Call (FnMut) for Foo");
}
}
impl FnOnce<()> for Foo {
type Output = ();
extern "rust-call" fn call_once(self, _args: ()) {
println!("Call (FnOnce) for Foo");
}
}
fn main() {
let x = Foo;
x();
}
Normally though, only one trait's implementation would have interesting code in it and the other trait implementations would delegate to it:
extern "rust-call" fn call(&self, args: ()) {
println!("Foo called, took args: {:?}", args);
}
// ...
extern "rust-call" fn call_mut(&mut self, args: ()) {
self.call(args)
}
// ...
extern "rust-call" fn call_once(self, args: ()) {
self.call(args)
}
See also:
Upvotes: 35