#hash-set #mutable #iter-mut

macro mut_set_derive

A safe implementation for HashSet with iter_mut and get_mut

15 releases

new 0.3.3 Apr 29, 2024
0.3.2 Apr 18, 2024
0.3.0 Mar 31, 2024
0.2.9 Mar 30, 2024
0.1.2 Mar 28, 2024

#7 in #hashset

Download history 827/week @ 2024-03-25 174/week @ 2024-04-01 3/week @ 2024-04-08 219/week @ 2024-04-15

1,223 downloads per month
Used in liberty-db

MIT license

25KB
578 lines

mut_set

License github crates.io Docs

Use the idea of readonly to implement HashSet with iter_mut and get_mut.

Add crates by following command

cargo add mut_set mut_set_derive

or add it into Cargo.toml

[dependencies]
mut_set = "0.3"
mut_set_derive = "0.3"

Demo

#[derive(Debug)]
#[mut_set_derive::item]
pub(super) struct MyItem<T1, T2>
where
    T1: Sized,
{
    #[id]
    pub id1: usize,
    pub ctx1: T1,
    pub(in crate::derive) ctx2: T2,
    #[id]
    pub id2: String,
}

#[test]
fn test() {
    let mut set = mut_set::MutSet::new();
    println!("{:?}", set);
    set.insert(MyItem {
        id1: 2,
        id2: "www".to_string(),
        ctx1: -1,
        ctx2: "ccc".to_string(),
    });
    set.insert(MyItem {
        id1: 1,
        id2: "ww".to_string(),
        ctx1: -2,
        ctx2: "cc".to_string(),
    });
    println!("{:?}", set);
    for v in set.iter() {
        println!("{:?}", v);
    }
    for v in set.iter_mut() {
        v.ctx1 = 0;
        println!("{:?}", v.id1);
        // In `iter_mut` IDs write will be prohibited
        // v.id1 = 0;
    }
    println!("{:?}", set);
    let id1 = MyItem::id(2, "www".to_string());
    println!("{:?}", set.get(&id1));
    for v in set.into_iter() {
        println!("{:?}", v);
    }
}

How does mut_set work

The macro will implement all stuffs in tests/src/basic_expand.rs.

Take Xxx as an example:

  • Create two struct ImmutIdXxx, and XxxId. Where ImmutIdXxx is same to Xxx with private id fields, and XxxId only contains id fields.
  • Do rearrangement so that all id fields are located at beginning of the structure. By the help of #[repr(C)], we can use raw pointer operations to (zero-cost?) convert Xxx, ImmutIdXxx, and XxxId.
  • impl mut_set::Item for Xxx<ImmutIdItem = ImmutIdXxx>
  • MutSet<T: Item> = HashMap<u64, T::ImmutIdItem>, where the u64 is the hash value.
  • Wrap the iteration function
    • iter(&self) -> Iter<&Xxx>
    • into_iter(self) -> Iter<Xxx>
    • iter_mut(&mut self) -> Iter<&mut ImmutIdXxx>

Other features

  • If you want to add some derive/proc_macro to ImmutIdXxx/XxxId. You can add arguments to mut_set_derive::item, to specify which derive should add to ImmutIdXxx/XxxId, and the filter for fileds attribute. e.g.,

    #[mut_set_derive::item(
    macro(derive(Debug, Clone);
          derive(derivative::Derivative);
          derivative(Default);),
    attr_filter(derivative;)
    )]
    struct Xxx {
        #[id]
        id: usize,
        #[derivative(Default(value = "8"))]
        #[some_attr]
        ctx: usize,
    }
    

    will impl

    #[derive(Debug, Clone)]
    #[derive(derivative::Derivative)]
    #[derivative(Default)]
    struct ImmutIdXxx {
        id: usize,
        #[derivative(Default(value = "8"))]
        ctx: usize,
    }
    
    #[derive(Debug, Clone)]
    #[derive(derivative::Derivative)]
    #[derivative(Default)]
    struct XxxId {
        id: usize,
    }
    

    Here some_attr is not in attr_filter(), so it will be removed. See more at tests/src/derive.rs and tests/src/derive_expand.rs.

Dependencies

~315–760KB
~18K SLoC