是否可以使用特征作为自由函数的语法糖?

时间:2017-02-15 13:36:58

标签: rust

我想根据外部数据调用函数, 像这样:

struct Foo {
    data: &'static str,
    handler: Option<fn (i32) -> String>,
}

fn aaa_converter(_: i32) -> String { unimplemented!(); }
fn bbb_converter(_: i32) -> String { unimplemented!(); }

fn main() {
    let _ = Foo{data: "aaa", handler: Some(aaa_converter)};
    let _ = Foo{data: "bbb", handler: Some(bbb_converter)};
    let _ = Foo{data: "ccc", handler: None};
}

我输入字符串&#34; aaa&#34;,我需要拨打aaa_converter。一切正常,我将Foo个对象放入哈希映射中,如果不是handler则调用正确的None

现在我有很多这样的转换器,我想用语言帮助处理它们。

理想情况下,会有这样的语法:

trait Handler {
    fn handle(a: i32) -> String;
}

impl Handler for "aaa" {
    // ...
}

我能拥有的最佳匹配是:

trait Handler {
    fn handle(/*&self, */a: i32) -> String;
}

struct aaa;

impl Handler for aaa {
    fn handle(/*&self, */a: i32) -> String {
        unimplemented!();
    }
}

struct Foo {
    data: &'static str,
    handler: &'static Handler,
}

fn main() {}

但是这样的代码无法编译:

the trait `Handler` cannot be made into an object
   = note: method `handle` has no receiver

How to call a trait method without a struct instance?看起来很相关,但答案中链接的RFC已过时。从那时起,语言中也可能发生了一些变化吗?

是否可以将特征用作指向自由函数的简单指针?

或者是否有另一种组织处理程序的方法?

1 个答案:

答案 0 :(得分:1)

这里有一个误解:

  1. Rust有功能
  2. Rust有&#34;仿函数&#34;
  3. Rust有特质
  4. 一个功能只是:

    fn i_am_a_function(a: i32) -> String { a.to_string() }
    

    仿函数是一个函数对象,它是与某个状态相关联的函数。 Rust实际上有3个:

    FnOnce(i32) -> String
    FnMut(i32) -> String
    Fn(i32) -> String
    

    最后Rust有这样的特点:

    trait Handler {
        fn non_object_safe(a: i32) -> String;
        fn object_safe(&self, a: i32) -> String;
    }
    

    特征可以在两种情况下使用:

    • 提供通用函数中的边界
    • 当对象安全时,提供类型擦除

    粗略地说,如果某个特征与其相关的任何功能都不是对象安全的:

    • 没有&self&mut self参数
    • 提及Self(类型)

    有关这两个概念的更多信息,请查看Rust Book。

    在您的情况下,您可以使用:

    1. 指向函数的简单指针:fn(i32) -> String
    2. 仿函数Fn(i32) -> String
    3. 对象安全特性
    4. 你唯一不能使用的是非物体安全特性,当然多亏了墨菲,它是你选择的唯一选择。

      在您的情况下,最简单的解决方案是使用对象安全特征:

      trait Handler {
          fn handle(&self, a: i32) -> String;
      }
      
      struct A;
      
      impl Handler for A {
          fn handle(&self, a: i32) -> String {
              a.to_string()
          }
      }
      
      const STATIC_A: &'static Handler = &A;
      
      struct Foo {
          data: &'static str,
          handler: &'static Handler,
      }
      
      fn main() {
          let foo = Foo { data: "aaa", handler: STATIC_A };
          println!("{}", foo.handler.handle(3));
      }
      

      如果数据指针的64位开销真的打扰你,那么你可以使用函数指针并构建自己的虚拟表:

      struct Handler {
          handle: fn(i32) -> String,
      }
      
      fn aaa(a: i32) -> String {
          a.to_string()
      }
      
      const STATIC_A: &'static Handler = &Handler { handle: aaa };
      
      struct Foo {
          data: &'static str,
          handler: &'static Handler,
      }
      
      fn main() {
          let foo = Foo { data: "aaa", handler: STATIC_A };
          println!("{}", (foo.handler.handle)(3));
      }
      

      它不那么符合人体工程学,但它也小了64位!