wvwwvwwv / scalable-concurrent-containers

High performance containers and utilities for concurrent and asynchronous programming
Apache License 2.0
314 stars 16 forks source link

request: Adopt equivalent::Equivalent and equivalent::Comparable #162

Open qthree opened 1 week ago

qthree commented 1 week ago

hashbrown crate uses Equivalent trait instead of Borrow for get operations. It's much more useful cause you can't return non-reference from Borrow::borrow, for example if you want to implement you own String/str combination (newtypes). Of course you can use bytemuck transparent derive to convert between &str and newtype reference, but that's not ideal, for example if want to add another field into query type. I propose to rewrite all scc methods to use new Equivalent trait instead of Borrow, and do blanket impl for Borrow as hashbrown does.

qthree commented 1 week ago

And for TreeIndex we can add another trait for Borrow + Ord, something like

pub trait Compare<K: ?Sized> {
    // Required method
    fn compare(&self, key: &K) -> std::cmp::Ordering;
}

impl<Q, K> Compare<K> for Q
where
    Q: Ord + ?Sized,
    K: Borrow<Q> + ?Sized,
{
    fn compare(&self, key: &K) -> std::cmp::Ordering {
        self.cmp(key.borrow())
    }
}
impl<K, V> TreeIndex<K, V> {
    pub fn peek<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<&'g V>
    where
        Q: Compare<K>,
    {...}
}
qthree commented 1 week ago

Actually, there is already crate with exactly those two traits, what's your policy for adding deps?

wvwwvwwv commented 1 week ago

Hi @qthree,

This looks a really good idea, thanks a lot. On the other hand, the crate seems way too trivial to be a dependency, so can you possibly use your own code in your comment (in compare.rs? + for the equivalent trait, you may have to state that the code is "inspired" or "copied" from the equivalent crate) instead of adding the crate to Cargo.toml?

qthree commented 1 week ago

@wvwwvwwv I can do It the same way hashbrown did it. Copy-paste Comparable and Equivalent traits into scc, in a new module, but then place this new module under #[cfg(not(feature = "equivalent")]. So if users want to they can enable equivalent dependency. For example if they want to use the same query type in different maps without reimplementing the same trait twice.