I'm coding a game with positions expressed as an Vec<u16>
. I'm caching best moves and losing positions for an exhaustive dfs. Best positions return a Guess
struct with a couple u16
members, while the losing positions include a single u16
reporting the depth at which it fails. I'm using a HashMap<Vec<u16>,Guess>
and a HashMap<Vec<u16>,u16>
to store and retrieve these. There are many overlapping functions including file saving and restoring, so I am trying to implement a generic Cache
struct to manage both types as follows:
trait Cacheable {}
impl Cacheable for u16 {}
impl Cacheable for Guess {}
pub struct Cache<T: Cacheable> {
hashmap: Vec<HashMap<Vec<u16>, T>>,
filename: String,
items_added: u32,
}
When loading the HashMap
from disk, I need to recover the hashmap value, but I can't find a way to create a function that returns type T
from the input Vec<u16>
. I've tried type-specific impl
like:
impl Cache<u16> {
fn make_value(data: &Vec<u16>) -> u16 {
data[0]
}
}
impl Cache<Guess> {
fn make_value(data: &Vec<u16>) -> Guess {
Guess::new_from_vec(data)
}
}
But the compiler complains about duplicate impl
for the same function, when I attempt value = Cache::make_value(&data);
. What is the idiomatic way to do this?
CodePudding user response:
Why do you try to write the generic-dependent make_value
in an impl Cache
?
You already have a trait that represents cacheable values, so use that one to represent the different behaviour. Why else would you have the trait?
Also, nitpick: Using &Vec<u16>
as a function parameter is kind of an anti-pattern. Use &[u16]
instead. It is completely compatible and more generic. There is no reason why one would ever need to use a &Vec<u16>
instead.
use std::collections::HashMap;
#[derive(Debug)]
struct Guess {
data: Vec<u16>,
}
impl Guess {
fn new_from_slice(data: &[u16]) -> Self {
Self {
data: data.to_vec(),
}
}
}
pub trait Cacheable: Sized {
fn make_value(data: &[u16]) -> Self;
}
impl Cacheable for u16 {
fn make_value(data: &[u16]) -> u16 {
data[0]
}
}
impl Cacheable for Guess {
fn make_value(data: &[u16]) -> Guess {
Guess::new_from_slice(data)
}
}
pub struct Cache<T: Cacheable> {
hashmap: Vec<HashMap<Vec<u16>, T>>,
filename: String,
items_added: u32,
}
impl<T: Cacheable> Cache<T> {
fn make_value(data: &[u16]) -> T {
T::make_value(data)
}
}
fn main() {
let data = vec![1, 2, 3];
let cached_u16: u16 = Cache::make_value(&data);
let cached_guess: Guess = Cache::make_value(&data);
dbg!(cached_u16);
dbg!(cached_guess);
}
[src/main.rs:48] cached_u16 = 1
[src/main.rs:49] cached_guess = Guess {
data: [
1,
2,
3,
],
}
CodePudding user response:
I realized I needed to implement the type-specific functions in the trait impl
. I was just using the trait to limit the applicability of the generic. Instead of what I had above, I needed to do this:
trait Cacheable<T> {
fn make_value(data: &Vec<u16>) -> T
}
impl Cacheable<u16> for u16 {
fn make_value(data: &Vec<u16>) -> u16 {
data[0]
}
}
impl Cacheable<Guess> for Guess {
fn make_value(data: &Vec<u16>) -> Guess {
Guess::new_from_vec(data)
}
}
Haven't tested yet, but it compiles now, so I'm optimistic. Thanks all for the input.