Giter Club home page Giter Club logo

Comments (4)

WalterSmuts avatar WalterSmuts commented on June 27, 2024 1

Does calling the macro from two different functions create two different objects? #3

Yes.

Am I misunderstanding something?

Yes, although I don't blame you. The docs could be more clear. Each occurrence of the get_or_init! macro causes a new static map to be initialised. I.e. a separate use of get_or_init! macro would refer to a separate singleton.

If so, how would I have to approach this, if I wanted such a generic "registry"-like singleton?

You need to factor the access into a single function. Something like:

use generic_singleton::get_or_init;
use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::sync::RwLock;

fn use_my_generic_singleton<T, U>(mut f: impl FnMut(&RwLock<HashMap<T, T>>) -> U) -> U
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    let my_generic_singleton = get_or_init!(|| RwLock::new(HashMap::<T, T>::new()));
    f(my_generic_singleton)
}

fn add_to_registry<T>(key: T, value: T) -> bool
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    use_my_generic_singleton(|registry| {
        if registry.read().unwrap().contains_key(&key) {
            false
        } else {
            registry.write().unwrap().insert(key, value);
            true
        }
    })
}

fn get_from_registry<T>(key: T) -> T
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    use_my_generic_singleton(|registry| {
        if !registry.read().unwrap().contains_key(&key) {
            panic!("No such key registered: {}", key)
        }
        *registry.read().unwrap().get(&key).unwrap()
    })
}

fn main() {
    assert!(add_to_registry::<u32>(1, 10));
    assert!(!add_to_registry::<u32>(1, 20));
    let val = get_from_registry::<u32>(1);
    assert_eq!(val, 10);
}

Although the above seems awfully complicated and I've not even convinced myself it makes sense. Will think about it some more but there's at least something for you for now.

I think I should probably update the docs or add a convenience method or perhaps both...

from generic_singleton.

daniil-berg avatar daniil-berg commented on June 27, 2024

@WalterSmuts Thanks a lot for the clarification and your example workaround.

Your suggestion of using a closure would probably be the most flexible in this scenario. I suppose, if all I want is the add/get functionality, I could also go the opposite route and put that logic in a single not-so-nice non-public function and have two friendly public interfaces for it like so: (Thinking about this in the context of library code.)

use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::sync::RwLock;

use generic_singleton::get_or_init;


trait MyItem: Eq + Copy + Display + Hash + Send + Sync + 'static {}

impl<T> MyItem for T
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static
{}


fn use_registry<T: MyItem>(key: T, value: Option<T>) -> T {
    let registry = get_or_init!(|| RwLock::new(HashMap::<T, T>::new()));
    match value {
        None => {
            if !registry.read().unwrap().contains_key(&key) {
                panic!("No such key registered: {}", key)
            }
            *registry.read().unwrap().get(&key).unwrap()
        }
        Some(value) => {
            if registry.read().unwrap().contains_key(&key) {
                panic!("Key already registered: {}", key)
            }
            registry.write().unwrap().insert(key, value);
            value
        }
    }
}

pub fn add_to_registry<T: MyItem>(key: T, value: T) -> T {
    use_registry(key, Some(value))
}

pub fn get_from_registry<T: MyItem>(key: T) -> T {
    use_registry(key, None)
}

fn main() {
    assert_eq!(add_to_registry::<u32>(1, 10), 10);
    assert_eq!(get_from_registry::<u32>(1), 10);
}

This also runs as expected.


I still wonder, if it is possible to somehow achieve something like what I expected in the first place. I am very inexperienced with Rust still. And I have not looked deeper into your code yet, so I don't know if this could be added in as an additional feature or something, i.e. being able to "share" that singleton across functions.

But maybe you can dismiss this idea right off the bat, if you know it isn't possible. Maybe I'll gain some more insight, after going through your code.

Either way, this crate is cool and useful to me as is.

from generic_singleton.

WalterSmuts avatar WalterSmuts commented on June 27, 2024

I still wonder, if it is possible to somehow achieve something like what I expected in the first place.

It is but it'll probably lead to unwanted outcomes... It was like that in version 1.3 but had to add the following documentation:

DO NOT USE WITH A TYPE YOUR CRATE DOES NOT PRIVATELY OWN!!! The map is shared across crates, so if you use a type that is publicly available, then another crate will be able to mutate your singleton, breaking whichever rules you’ve got in place and vice-versa. Use the new type pattern if you need to use a public struct in the map.

It's a bit of a foot-gun for users where the workaround is just creating your own wrapping function (This is probably what you wanted BTW; ignore the other example):

use std::collections::HashMap;
use std::fmt::Display;
use std::hash::Hash;
use std::sync::RwLock;

use generic_singleton::get_or_init;

fn get_my_generic_singleton<T>() -> &'static RwLock<HashMap<T, T>>
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    get_or_init!(|| RwLock::new(HashMap::<T, T>::new()))
}

fn add_to_registry<T>(key: T, value: T) -> bool
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    let registry = get_my_generic_singleton();
    if registry.read().unwrap().contains_key(&key) {
        false
    } else {
        registry.write().unwrap().insert(key, value);
        true
    }
}

fn get_from_registry<T>(key: T) -> T
where
    T: Eq + Copy + Display + Hash + Send + Sync + 'static,
{
    let registry = get_my_generic_singleton();
    if !registry.read().unwrap().contains_key(&key) {
        panic!("No such key registered: {}", key)
    }
    *registry.read().unwrap().get(&key).unwrap()
}

fn main() {
    assert!(add_to_registry::<u32>(1, 10));
    assert!(!add_to_registry::<u32>(1, 20));
    let val = get_from_registry::<u32>(1);
    assert_eq!(val, 10);
}

from generic_singleton.

daniil-berg avatar daniil-berg commented on June 27, 2024

@WalterSmuts Ah, of course, just grabbing the singleton from a separate function makes much more sense. Thank you.

Interesting side note about the 🦶 🔫 with public types. Good to know that this is no longer an issue.

from generic_singleton.

Related Issues (2)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.