如何在Rust中制作调度表?

时间:2018-07-17 03:12:11

标签: rust

我正在尝试构建一个简单的RPN计算器,并且已经具备了基本功能。我想制作一个dispatch table来实现各种计算器功能。如果我在Perl中这样做,我会写类似这样的东西:

my %ops = (
  '+' => sub { +shift + +shift; },
  '-' => sub { +shift - +shift; },
  '*' => sub { +shift * +shift; },
  '/' => sub { +shift / +shift; }
);

或使用JavaScript:

let ops = {
    "+": (a, b) => a + b,
    "-": (a, b) => a - b,
    "*": (a, b) => a * b,
    "/": (a, b) => a / b
};

这是我到目前为止在Rust中尝试过的方法:

use std::collections::HashMap;

fn main() {
    println!("Going to call +");

    let dispatch = HashMap::new();

    dispatch.insert(String::from("+"), |a, b| a + b);
    dispatch.insert(String::from("-"), |a, b| a - b);

    let plus = dispatch.get(&String::from("+"));
    println!("2 + 3 = {}", plus(2, 3));

    let minus = dispatch.get(&String::from("-"));
    println!("2 - 3 = {}", minus(2, 3));
}

当我尝试编译时,出现以下错误:

error[E0308]: mismatched types
 --> src/main.rs:9:40
  |
9 |     dispatch.insert(String::from("-"), |a, b| a - b);
  |                                        ^^^^^^^^^^^^ expected closure, found a different closure
  |
  = note: expected type `[closure@src/main.rs:8:40: 8:52]`
             found type `[closure@src/main.rs:9:40: 9:52]`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object

error[E0618]: expected function, found enum variant `plus`
  --> src/main.rs:12:28
   |
11 |     let plus = dispatch.get(&String::from("+"));
   |         ---- `plus` defined here
12 |     println!("2 + 3 = {}", plus(2, 3));
   |                            ^^^^^^^^^^ not a function
help: `plus` is a unit variant, you need to write it without the parenthesis
   |
12 |     println!("2 + 3 = {}", plus);
   |                            ^^^^

error[E0618]: expected function, found enum variant `minus`
  --> src/main.rs:15:28
   |
14 |     let minus = dispatch.get(&String::from("-"));
   |         ----- `minus` defined here
15 |     println!("2 - 3 = {}", minus(2, 3));
   |                            ^^^^^^^^^^^ not a function
help: `minus` is a unit variant, you need to write it without the parenthesis
   |
15 |     println!("2 - 3 = {}", minus);
   |                            ^^^^^

“没有两个闭包,即使相同,也没有相同的类型”是什么意思?我怎样才能使HashMap持有一个闭包,然后调用它?

听起来像使用Box可以解决此问题...就像我说的那样,我很新,而且我还没有使用Box。我如何开箱即用?

1 个答案:

答案 0 :(得分:4)

这里有一些正交的问题。首先,您的哈希图是不可变的。您可以使用let mut而不是let mut,但这是一个好习惯,但是为了能够插入其中,我们需要(至少在最初)将其设为let mut。如果您打算在初始构造后修改哈希图,则可能还需要dispatch let dispatch = { let mut temp = HashMap::new(); temp.insert(String::from("+"), |a, b| a + b); temp.insert(String::from("-"), |a, b| a - b); temp }; 变量。

fn(i32, i32) -> i32

现在,您需要哈希表的显式类型。就编译器而言,您定义的两个闭包是完全不同的类型。但是,它们都与i32(在i32上的二进制函数的类型兼容(如果需要,可以用不同的数字类型替换let dispatch = { let mut temp: HashMap<String, fn(i32, i32) -> i32> = HashMap::new(); temp.insert(String::from("+"), |a, b| a + b); temp.insert(String::from("-"), |a, b| a - b); temp }; ),因此,让我们使类型明确。

HashMap.get

最后,get返回一个std::option::Option,而不是直接值,因此我们需要解开它。如果找不到密钥,则None返回expect。如果这是一个大型项目,则可以通过记录错误或告诉用户来适当地处理该错误,但是对于类似这样的简单操作,我们只需要使用let plus = dispatch.get(&String::from("+")).expect("Couldn't find +"); ,它实际上告诉编译器“是的,我知道这可能会出错,我会无视这一事实。”对于我们的简单示例来说,这是非常好的。

let minus = dispatch.get(&String::from("-")).expect("Couldn't find -");
use std::collections::HashMap;

fn main() {
    let dispatch = {
        let mut temp: HashMap<String, fn(i32, i32) -> i32> = HashMap::new();
        temp.insert("+".into(), |a, b| a + b);
        temp.insert("-".into(), |a, b| a - b);
        temp
    };

    let plus = dispatch["+"];
    println!("2 + 3 = {}", plus(2, 3));

    let minus = dispatch["-"];
    println!("2 - 3 = {}", minus(2, 3));
}

完整示例

String

请注意,您可以将&'static str替换为let dispatch = { let mut temp: HashMap<_, fn(i32, i32) -> i32> = HashMap::new(); temp.insert("+", |a, b| a + b); temp.insert("-", |a, b| a - b); temp };

{{1}}