1

I would like to have a struct:

struct AllCaches {
    vectors: HashMap<usize, Cache<T>>,
}

The purpose of AllCaches is to hold various bits of useful information relating to Caches which are typed (<T>).

Clearly, this isn't valid code, since I would need to specify <T> in my AllCaches declaration. I can't do that since the AllCaches struct is intended to hold all my caches irrespective of their <T>.

My current solution involves a trait which I implement as:

struct AllCaches {
    vectors: HashMap<usize, Box<dyn CacheTrait>>,
}

I have managed to make this work, but it involves a lot of nasty/messy down casting and horrible looking code such as:

let data;
if let Some(thing) = caches.downcast_ref::<Cache<u64>>() {
    debug!("U64");
    data = VectorType::U64(thing.populate_vector(&req.index, &mvector));
} else if let Some(thing) = caches.downcast_ref::<Cache<f64>>() {
    debug!("F64");
    data = VectorType::F64(thing.populate_vector(&req.index, &mvector));
}

(using downcast-rs crate to achieve the down casting).

The impact on my code is that I need a lot of very repetitive (<T>) specific functionality and the benefit of having a generic type seems to be lost.

This seems horrible to me and I feel like there must be a better answer.

I've wrestled with this for a week (on and off) and investigated other options such as using enums, but that didn't seem to improve the situation (although, maybe I didn't understand that approach completely). I've done a lot of googling and reading documentation, I'm fairly inexperience with Rust, but not made much progress.

hellow
  • 12,430
  • 7
  • 56
  • 79
garypen
  • 91
  • 1
  • 6
  • 1
    This is interesting but I suspect this is a XY problem and that a loosely typed `allCaches` struct isn't the right data structure for your (unspecified) need. – Denys Séguret Mar 20 '19 at 09:10
  • Ok. That's possible. However, my problem seems to be fairly clearly specified. I have a struct which I'd like to use to organise my data and as part of that organisation I'd like to hold a HashMap of parameterised types. I have an approach that works but is ugly and I'd like to know if there is a better approach. – garypen Mar 20 '19 at 09:43
  • The better approach in my opinion would be to avoid the mess of loosely typed maps. Rust is a typed language and you're trying very hard to code as in an loosely typed one. A solution might be to define several maps with their types. – Denys Séguret Mar 20 '19 at 09:51
  • 1
    I think the example needs a little more detail, to define the type of `caches`, define what *exactly* `downcast_ref` and `populate_vector` do, eliminate the undefined `debug!` macro, etc. A [mcve] is the goal. I have some ideas for improvements, but I don't understand what the snippet is doing. – trent Mar 20 '19 at 11:49
  • @trentcl The problem is really a structural problem and that makes it hard to give an MCV example. My comment above tries to summarise why I'm doing what I'm doing. It could be that Denys' comment is correct and I need to clarify my thoughts a little better and approach this differently. I'll try out some more ideas and see if I can provide a better example. – garypen Mar 20 '19 at 13:05
  • 2
    I believe your question is answered by the answers of [How can I avoid a ripple effect from changing a concrete struct to generic?](https://stackoverflow.com/q/44912349/155423). If you disagree, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Mar 20 '19 at 13:13
  • 1
    I'll add that if there is an exhaustive, finite-sized list ("closed set") of all the possible types that `T` can be, 99% of the time you want an `enum`. Trait objects are for when you have an "open set"; i.e., you can't enumerate all the types that `T` might be either because you don't have control over them or because you want to leave your API open to future expansion without affecting usage sites. A chain of `if is_this_type... else if is_that_type ... else if ...` is just an awkward and *not-compile-time-checked* `match` on what is effectively an `enum`. – trent Mar 20 '19 at 13:36
  • 1
    Using enums is better, wrt the checking aspects. I just can't find a good example of how to use an enum with a generic type. I think the reference that @Shepmaster makes to "avoiding ripple" will help me figure out a better way to implement a solution. Thanks for the advice. – garypen Mar 20 '19 at 14:02

0 Answers0