If you need a unique ID with a very low probability of a match, like other said, you can use a 128 bit ID generator, such as the rand or getrandom libraries, plus a 128 bit type (Rust lib's UUID also uses this generator and is 128 bit, just in a different format). Now how to use it at compile time, as const functions have many limitations and can not yet to call this. The good news is that the generator code can be called standalone without any dependencies on your other code. So you can generate the ID at compile time with a proc macro, then the proc macro also rewrite your code to insert the generated constant ID
Also, since you've already mentioned that you want to create a tool to memoize function calls, is the call static only the first time, or can it be dynamic, meaning it can be called multiple times with different parameters and you want the cache to still catch it?
If it's static just once, as others have mentioned, you can use std oncecell
But if it's dynamic, you need heap allocation that allows you to retrieve the value by key to store the cache collection, for example, a hashmap
I created something like this. You can also add, for example, a maximum storage limit, overriding one of the caches when it's full, or other strategies that suit your needs, and change the key to use ID generator
use std::collections::HashMap;
use std::hash::Hash;
pub struct MemoizeSingleThreaded<T, U, F>
where
T: Hash + Eq,
U: Clone,
F: FnMut(&T) -> U,
{
logic: F,
cache: HashMap<T, U>,
max_size: usize,
}
impl<T, U, F> MemoizeSingleThreaded<T, U, F>
where
T: Hash + Eq + Clone,
U: Clone,
F: FnMut(&T) -> U,
{
pub fn new(max_size: usize, logic: F) -> Self {
Self {
logic,
cache: HashMap::with_capacity(max_size),
max_size,
}
}
pub fn call(&mut self, arg: T) -> U {
if let Some(result) = self.cache.get(&arg) {
return result.clone();
}
let result = (self.logic)(&arg);
if self.cache.len() >= self.max_size {
let key_to_remove = self.cache.keys().next().cloned();
if let Some(k) = key_to_remove {
self.cache.remove(&k);
}
}
self.cache.insert(arg, result.clone());
result
}
}
ftest::test!(memoize_single_thrraded_test, {
test_memoize {
let mut m = MemoizeSingleThreaded::new(10, |&(a, b): &(i32, i32)| {
a * b
});
let a = [
(2, 3, 6),
(8, 2, 16),
(9, 9, 81)
];
for (a, b, c) in a {
let res = m.call((a, b));
assert_eq!(res, c);
}
}
});