How can I make a caching smart pointer in rust?
So that any equivalent objects can be handled as references of a single instance of that object, in the end I want to be able to take advantage of std::ptr::eq
for equivalence-checking, instead of having to use PartialEq
all the time.
I guess I want to be able to do something like this:
let object = String::from("hello");
let first = One::new(object);
let second = One::new(object);
assert!(std::ptr::eq(first, second));
Here's my attempt at making such a smart pointer
use std::collections::HashMap;
use std::hash::Hash;
trait OneKeyTrait: Hash + Sized {}
static mut cache: HashMap<dyn OneKeyTrait, One<dyn OneKeyTrait>> = HashMap::new();
pub struct One<T: OneKeyTrait> {
count: usize,
value: T,
}
impl<T: OneKeyTrait> One<T> {
fn new(x: T) -> One<T> {
if cache.contains_key(&x) {
cache.get_mut(&x).count += 1;
cache.get(&x)
} else {
let one_x = One {
count: 1,
value: x,
};
cache.insert(x, one_x);
one_x
}
}
}
impl<T: OneKeyTrait> std::ops::Deref for One<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: OneKeyTrait> Drop for One<T> {
fn drop(&mut self) {
let mut one_x = cache.get_mut(&self.value);
one_x.count -= 1;
if one_x.count == 0 {
cache.remove(&self.value);
}
}
}
fn main() {
let object = String::from("hello");
let first = One::new(object);
let second = One::new(object);
assert!(std::ptr::eq(first, second));
}
But it won't compile because
error[E0277]: the size for values of type `(dyn OneKeyTrait + 'static)` cannot be known at compilation time
--> one.rs:6:19
|
6 | static mut cache: HashMap<dyn OneKeyTrait, One<dyn OneKeyTrait>> = HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn OneKeyTrait + 'static)`
note: required by a bound in `HashMap`
error[E0038]: the trait `OneKeyTrait` cannot be made into an object
--> x.rs:6:48
|
6 | static mut cache: HashMap<dyn OneKeyTrait, One<dyn OneKeyTrait>> = HashMap::new();
| ^^^^^^^^^^^^^^^ `OneKeyTrait` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> one.rs:4:27
|
4 | trait OneKeyTrait: Hash + Sized {}
| ----------- ^^^^^ ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
= help: consider moving `hash` to another trait