use LazyLock to make static variable, but that can only make immutable access to the map, so use Mutex to gain mutable access to the map, as for the Box change it to Arc to make it able to get over borrow checker, since if you want to borrow from static variable, it get weird, and since you don't need mutable reference to the object, Arc is good enough
Example
mod lib {
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, LazyLock, Mutex};
pub trait IFoo: Sync + Send + Debug + 'static {}
#[derive(Debug)]
pub struct Foo {
pub key: String,
}
static IFOO_REGISTRY: LazyLock<Mutex<HashMap<String, Arc<dyn IFoo>>>> =
LazyLock::new(|| Default::default());
// make it private to make sure user cannot access it and modify it
// this doesn't need to memeber of Foo
pub fn get_instance<T: IFoo>(
key: String,
factory: impl Fn(String) -> T,
) -> Arc<dyn IFoo> {
let mut registry = IFOO_REGISTRY.lock().unwrap();
match registry.get(&key) {
Some(a) => a.clone(),
None => {
let arc = Arc::new(factory(key.clone()));
registry.insert(key.clone(), arc.clone());
arc
}
}
}
pub fn print_registry() {
let registry = IFOO_REGISTRY.lock().unwrap();
println!("Registry {{");
for (k, v) in registry.iter() {
println!(" {k} : {v:?}")
}
println!("}}");
}
}
use lib::*;
#[derive(Debug)]
struct MyFoo(Foo);
impl MyFoo {
pub fn new(key: String) -> Self {
Self(Foo { key })
}
}
impl IFoo for MyFoo {}
fn main() {
print_registry();
// Registry {
// }
let instance = get_instance("myfoo".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo" })
print_registry();
// Registry {
// myfoo : MyFoo(Foo { key: "myfoo" })
// }
// second call
let instance = get_instance("myfoo".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo" })
let instance = get_instance("myfoo2".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo2" })
print_registry();
// Registry {
// myfoo2 : MyFoo(Foo { key: "myfoo2" })
// myfoo : MyFoo(Foo { key: "myfoo" })
// }
}
Edit
updated to get rid of function type param, change to FnOnce, and Box<T>
mod lib {
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, LazyLock, Mutex};
pub trait IFoo: Sync + Send + Debug + 'static {}
#[derive(Debug)]
pub struct Foo {
pub key: String,
}
static IFOO_REGISTRY: LazyLock<Mutex<HashMap<String, Arc<Box<dyn IFoo>>>>> =
LazyLock::new(|| Default::default());
// make it private to make sure user cannot access it and modify it
// this doesn't need to memeber of Foo
pub fn get_instance(
key: String,
factory: impl FnOnce(String) -> Box<dyn IFoo>,
) -> Arc<Box<dyn IFoo>> {
let mut registry = IFOO_REGISTRY.lock().unwrap();
match registry.get(&key) {
Some(a) => a.clone(),
None => {
let arc = Arc::new(factory(key.clone()));
registry.insert(key.clone(), arc.clone());
arc
}
}
}
pub fn print_registry() {
let registry = IFOO_REGISTRY.lock().unwrap();
println!("Registry {{");
for (k, v) in registry.iter() {
println!(" {k} : {v:?}")
}
println!("}}");
}
}
use lib::*;
#[derive(Debug)]
struct MyFoo(Foo);
impl MyFoo {
pub fn new(key: String) -> Box<Self> {
Box::new(Self(Foo { key }))
}
}
impl IFoo for MyFoo {}
fn main() {
print_registry();
// Registry {
// }
let instance = get_instance("myfoo".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo" })
print_registry();
// Registry {
// myfoo : MyFoo(Foo { key: "myfoo" })
// }
// second call
let instance = get_instance("myfoo".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo" })
let instance = get_instance("myfoo2".to_string(), |k| MyFoo::new(k));
println!("{:?}", instance);
// MyFoo(Foo { key: "myfoo2" })
print_registry();
// Registry {
// myfoo2 : MyFoo(Foo { key: "myfoo2" })
// myfoo : MyFoo(Foo { key: "myfoo" })
// }
}
FooandIFoothat is needed to for user to use?get_instancechecks for existing key and returns the value (instance) if found, otherwise it calls the user-supplied factory method to create the instance, store in the map and return it.get_instancewill be called, I'm looking for implementation forFoo