2017-02-15 23 views
0

我想调用函数依赖于外部数据, 这样的:是否可以使用特征作为免费函数的语法糖?

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

我作为输入字符串“AAA”,我需要调用aaa_converter。所有工作正常,我把Foo对象放入哈希映射中,如果不是None,则调用合适的handler

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

理想情况下,将语法如下:

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

*为什么*?为什么不只是有一个空的结构,采取'self'参数,而不是使用它? – Shepmaster

+1

@Shepmaster:为什么要用特质开始? ;) –

+0

@MatthieuM。我的精神感觉告诉我,有时他们会想要状态。例如,'Foo'可能是实际的处理程序,'data'应该通过'self'来访问。 – Shepmaster

回答

1

有一个误区,在这里:

  1. 锈病具有以下功能:
  2. 锈有 “仿函数”
  3. 锈病性状

一个功能就是:

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

仿函子是一个函数ob ject,这是与某个状态相关的功能。拉斯特实际上有其中3:

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

最后锈病性状:

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. 对象的安全特征

唯一你不能使用是一个非对象安全的特性,当然,由于墨菲,这是唯一的选择你选了。

在你的情况下,最简单的解决方法是使用一个对象的安全特性:

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位!

+0

不,正如我指出的那样,我已经使用了这样的代码。问题是我有许多像'aaa'这样的函数并对它们进行组织我需要类似特性的东西来简化在代码中找到合适的功能并向其他人展示应实现的接口 – user1244932

+0

已更新;在这种情况下,我建议使用trait对象,除非你真的有严格的要求,但是以防万一我展示了如何自己创建一个虚拟表。 –

相关问题