В главе 13 книги Rust вы реализуете Cacher
, чтобы использовать памятку для демонстрации функционального программирования и ускорения выполнения долгосрочных задач. В качестве дополнительной задачи они рекомендуют разрешить Cacher
использование нескольких ключей с использованием HashMap
, а также использовать универсальные шаблоны для обеспечения большей гибкости.
Попробуйте изменить Cacher
, чтобы она содержала хеш-карту, а не одно значение.
Ключами хэш-карты будут значения arg
, которые передаются,
и значения хэш-карты будут результатом вызова
закрытие на этом ключе. Вместо того, чтобы смотреть self.value
напрямую
имеет значение Some
или None
, функция значения будет искать arg
в
хэш-карту и вернуть значение, если оно присутствует. Если это не
в настоящее время Cacher
вызовет замыкание и сохранит полученное значение
в хэш-карте, связанной с его значением arg
.
Вторая проблема текущей реализации Cacher
заключается в том, что она
принимает только замыкания, которые принимают один параметр типа u32
и возвращают
u32
. Мы могли бы хотеть кэшировать результаты замыканий, которые принимают строку
например, нарезать и вернуть usize
значения. Чтобы решить эту проблему, попробуйте
введение более общих параметров для повышения гибкости
Cacher
функциональность.
Мне удалось реализовать HashMap
, однако при попытке заменить определение замыкания u32
универсальным типом и использовать его в качестве сигнатуры HashMap
я столкнулся с проблемой.
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::thread;
use std::time::Duration;
struct Cacher<'a, T>
where
T: Fn(&'a u32) -> &'a u32,
{
calculation: T,
values: HashMap<&'a u32, &'a u32>,
}
impl<'a, T> Cacher<'a, T>
where
T: Fn(&'a u32) -> &'a u32,
{
fn new(calculation: T) -> Cacher<'a, T> {
Cacher {
calculation,
values: HashMap::new(),
}
}
fn values(&mut self, arg: &'a u32) -> &'a u32 {
match self.values.entry(arg) {
Entry::Occupied(e) => &*e.into_mut(),
Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)),
}
}
}
fn generate_workout(intensity: u32, random_number: u32) {
let mut expensive_result = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
&num
});
if intensity < 25 {
println!("Today, do {} pushups!", expensive_result.values(&intensity));
println!("Next, do {} situps!", expensive_result.values(&intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_result.values(&intensity)
);
}
}
}
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
generate_workout(simulated_user_specified_value, simulated_random_number);
}
Я попробовал K, V
генерики, как показано ниже, и они жалуются, когда Expected one of 7 possible values here
указывает на определение первого типа.
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::thread;
use std::time::Duration;
struct Cacher<'a, T: 'a, K: 'a, V: 'a>
where
T: Fn(&'a K) -> &'a V,
K: Hash + Eq,
{
calculation: T,
values: HashMap<&'a K, &'a V>,
}
impl<'a, T: 'a, K: 'a, V: 'a> Cacher<'a, T: 'a, K: 'a, V: 'a>
where
T: Fn(&'a K) -> &'a V,
K: Hash + Eq,
{
fn new(calculation: T) -> Cacher<'a, T: 'a, K: 'a, V: 'a> {
Cacher {
calculation,
values: HashMap::new(),
}
}
fn values(&mut self, arg: &'a K) -> &'a V {
match self.values.entry(arg) {
Entry::Occupied(e) => &*e.into_mut(),
Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)),
}
}
}
fn generate_workout(intensity: u32, random_number: u32) {
let mut expensive_result = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
&num
});
if intensity < 25 {
println!("Today, do {} pushups!", expensive_result.values(&intensity));
println!("Next, do {} situps!", expensive_result.values(&intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_result.values(&intensity)
);
}
}
}
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
generate_workout(simulated_user_specified_value, simulated_random_number);
}
Приводит к следующей ошибке:
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `:`
--> src/main.rs:16:39
|
16 | impl<'a, T: 'a, K: 'a, V: 'a> Cacher<T: 'a, K: 'a, V: 'a>
| ^ expected one of 7 possible tokens here
Является ли единственный способ добавить еще 2 дженерика (т.е. K
, V
) или есть способ повторно использовать один дженерик? Если 2 требуется, что я пропускаю выше?
Есть ли более идиоматический подход к решению этой проблемы? К сожалению, книга Rust не предлагает решения.