Storing any Sized monomorphism of a generic in a BTreeMap


#1

I have a struct pub struct PhysicalMapping<T> that I want to store in a BTreeMap, but I want to store any mapping (kind of forall T if that makes sense). The obvious way (I thought) was to use Any:

BTreeMap<PhysicalAddress, PhysicalMapping<Any>>

But this gives this error:

error[E0277]: the trait bound `core::any::Any + 'static: core::marker::Sized` is not satisfied
  --> src/acpi_handler.rs:16:5
   |
16 |     mapped_regions      : BTreeMap<PhysicalAddress, PhysicalMapping<Any>>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `core::any::Any + 'sta
tic` does not have a constant size known at compile-time
   |
   = help: the trait `core::marker::Sized` is not implemented for `core::any::Any + 'static`
note: required by `memory::paging::mapper::PhysicalMapping`
  --> src/memory/paging/mapper.rs:25:1
   |
25 | pub struct PhysicalMapping<T>
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Can I specify that T can be any Sized type, or is this not possible?


#2

You would need to make a trait that defines the ways you intend to interact with the forall T physical mappings after having placed them into the map.

use std::collections::BTreeMap;

struct PhysicalMapping<T> {
    value: T,
}

trait AnyPhysicalMapping {}
impl<T> AnyPhysicalMapping for PhysicalMapping<T> {}

fn main() {
    type PhysicalAddress = usize;
    let mut map = BTreeMap::<PhysicalAddress, Box<AnyPhysicalMapping>>::new();
    map.insert(1, Box::new(PhysicalMapping { value: "string" }));
    map.insert(2, Box::new(PhysicalMapping { value: true }));
}

#3

If the mapping is opaque and caller is to determine the type upon retrieval, you can do something with Any like:

use std::any::Any;
use std::collections::BTreeMap;

#[derive(Debug)]
struct PhysicalMapping<T> {
    value: T,
}

struct Mapping {
    map: BTreeMap<usize, Box<Any>>,
}

impl Mapping {
    fn insert<T: 'static>(&mut self, key: usize, val: PhysicalMapping<T>) {
        self.map.insert(key, Box::new(val));
    }

    fn get<T: 'static>(&self, key: usize) -> Option<&PhysicalMapping<T>> {
        self.map.get(&key).map_or(None, |a| a.downcast_ref())
    }
}

fn main() {
    let mut map = Mapping {
        map: BTreeMap::new(),
    };
    map.insert(1, PhysicalMapping { value: "string" });
    map.insert(2, PhysicalMapping { value: true });

    println!("{:?}", map.get::<&str>(1));
    println!("{:?}", map.get::<bool>(2));
}

The gist is you need to use trait objects to provide sizing - a raw trait isn’t going to work. In the above it’s using owned trait objects, but you can also use &Any if you want to store references (Mapping then gains a generic lifetime parameter, of course).