How do I create a closure that takes either u32 or &str and returns u32 or usize respectively?
This is from chapter 13.1 of the Rust book, where we use closures, memoization and generic types. In this example I can already pass two different closures to the struct (Cacher
) and get the values accordingly, but they have two be two different instances of the struct in order to handle the types correctly.
Can I build a generic type so the same instance of the struct can handle receiving either type and give me the value accordingly? Either the same u32
I passed, or the length of the &str
I passed.
use std::collections::HashMap;
use std::thread;
use std::time::Duration;
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
generate_workout(simulated_user_specified_value, simulated_random_number);
}
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.value(intensity));
println!("Next, do {} situps!", expensive_result.value(intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_result.value(intensity)
);
}
}
}
struct Cacher<T, U, V>
where
T: Fn(U) -> V,
{
calculation: T,
values: HashMap<U, Option<V>>,
}
impl<T, U, V> Cacher<T, U, V>
// Generic T is the function where U and V are the parameter and return value respectively for said function.
where
T: Fn(U) -> V,
U: std::cmp::Eq // U must have 3 trait bounds, Eq, Hash, and Copy.
+ std::hash::Hash
+ Copy,
V: Copy, // V must have trait bound Copy.
{
fn new(calculation: T) -> Cacher<T, U, V> {
Cacher {
calculation,
values: HashMap::new(),
}
}
fn value(&mut self, arg: U) -> V {
match self.values.get(&arg) {
// Get the value for the key in the hashmap.
Some(v) => v.unwrap(), // get the value in the option stored in the hashmap.
None => {
let v = (self.calculation)(arg);
self.values.insert(arg, Some(v)); // Put the key/value pair into the hashmap.
v
}
}
}
}
#[cfg(test)]
mod tests {
use super::Cacher;
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let _v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
#[test]
fn call_with_string_usize() {
let mut c = Cacher::new(|a: &str| a.len());
let v1 = c.value("Three");
assert_eq!(v1, 5);
}
}
I tried building structs for the value
field of the struct and then implementing a trait of that would group them:
struct CStr<'a> {
value: &'a str,
}
struct CInt {
value: u32,
}
trait CValue {
fn get<W: Copy + Eq>(&self) -> W;
}
impl<'a> CValue for CStr<'a> {
fn get<W: Copy + Eq>(&self) -> W {
self.value.len()
}
}
So this test would pass
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a.get());
let v1 = c.value(1);
let v2 = c.value("two");
assert_eq!(v2, 3);
}
but I am stuck here:
impl<'a> CValue for CStr<'a> {
fn get<W: Copy + Eq>(&self) -> W {
self.value.len() // here
}
}
with the error:
[rustc E0308] [E] mismatched types expected type parameter `W` found type `usize`
from Recent Questions - Stack Overflow https://ift.tt/3606NC8
https://ift.tt/eA8V8J
Comments
Post a Comment