如何使结构可调用?

时间:2017-03-17 13:53:18

标签: rust implementation traits

#![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();
}

我实现了Add特征,但我不明白如何将结构作为函数调用。我收到错误:

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

我是Rust的新手,无法找到如何使这件事发生的例子。

2 个答案:

答案 0 :(得分:14)

你还不能在稳定的Rust中实现Fn*特征。这只适用于使用#[feature]的夜间编译器!

完全阅读您正在实施的特征以了解如何实现它非常有用。 Fn trait定义为:

pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

请注意实现和定义之间的任何差异?我看到很多:

  1. 实施没有为Args提供价值!这就是编译器指向的内容。另请参阅Wrong number of type arguments: expected 1 but found 0

  2. 实现并没有实现supertrait FnMut,它本身需要supertrait FnOnceFnOnce是声明关联类型 Output的地方。

  3. 实现忽略了定义具体类型Output应该是什么。

  4. 当特征返回Self时,实现返回Self::Output

  5. 实施不接受call的第二个参数。该参数包含传入的任何参数。

  6. 此外,Rust中的类型使用PascalCase,而不是snake_case,因此它应为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();
    }
    

    通常情况下,只有一个特质的实现会在其中包含有趣的代码,而其他特征实现会委托给它:

    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)
    }
    

    另见:

答案 1 :(得分:2)

通过实现 Deref,您可以模拟所需的行为。然后你只需在参数周围使用括号并获得返回值,就像调用函数一样。

请参阅此主题:https://users.rust-lang.org/t/callable-struct-on-stable/54689/6

相关部分(感谢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
    }
}

这是我自己代码中的一个不完整示例,它采用参数,一个是引用,一个是可变引用。就我而言,闭包存储在 Rc 中,这需要奇数行 &(*self.function_container),因为我需要先进入 Rc,然后才能获得对闭包的引用。

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)
  }
}