Как мне сделать диспетчерскую таблицу в Rust?
Я пытаюсь построить простой калькулятор RPN, и у меня есть основы работы. Я хотел бы сделать таблицу диспетчеризации для реализации различных функций калькулятора. Если бы я делал это на 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 ответ
Здесь есть несколько ортогональных вопросов. Прежде всего, ваш hashmap неизменен. Ты используешь let
вместо 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
};
Теперь вам нужен явный тип для вашего hashmap. Два определения, которые вы определили для компилятора, имеют совершенно разные типы. Тем не менее, они оба совместимы с 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
возвращает std::option::Option
, а не прямое значение, поэтому нам нужно развернуть его. get
возвращается 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
};