< 返回版块

林深好材 发表于 2024-10-29 18:00

#[derive(Debug, Clone)]
pub struct Schema {
    name: String,
    sections: Vec<String>,
}

pub struct SchemaRegistry(HashMap<String, Schema>);

impl SchemaRegistry {
    pub fn new() -> Self {
        SchemaRegistry(HashMap::new())
    }

    pub fn register<'a>(&mut self, schema: Schema) -> &mut Self {
        self.0.insert(schema.name.clone(), schema);
        self
    }
}

评论区

写评论
LazyBoy 2024-11-04 15:54

因为Schema具有name: String的所有权,而插入map会导致schema发生移动,所以& schema.name直接引用是不行的。这时候就要考虑作为K的类型的特点了,String虽然可以被移动,但是其内部有固定不变的数据位置。所以,可以用点魔法,做点unsafe的东西,安全性需要设计者自行考虑:


use std::hash::Hash;
use std::{ptr::slice_from_raw_parts, str};
use std::{collections::HashMap, mem::ManuallyDrop};


fn main() {
}


#[derive(Debug)]
pub struct Schema {
    name: String,
    _sections: Vec<String>,
}
pub struct SchemaRegistry<S: Sharing>(HashMap<S, Schema>);


pub trait Sharing {
    /// 依赖于String内部指针的固定特性,创建一个间接共享数据,不具有原始内容的所有权
    unsafe fn from_shared(s: &String) -> Self;
}
/// 方案一比较直观,借用String内部指针,自己构建一个String对象,但是要屏蔽所有权(drop)
#[repr(transparent)]
#[derive(Hash, PartialEq, Eq, Debug)]
struct SharingString(ManuallyDrop<String>);
impl Sharing for SharingString {
    unsafe fn from_shared(s: &String) -> Self {
        let shared = ManuallyDrop::new(String::from_raw_parts(s.as_ptr() as *mut u8, s.len(), s.capacity()));
        SharingString(shared)
    }
}
/// 方案二不考虑所有权,同样的,构造间接&str引用,生命周期作为unsafe的保证,可以直接上'static
#[repr(transparent)]
#[derive(Hash, PartialEq, Eq, Debug)]
struct SharingStr(&'static str);
impl Sharing for SharingStr {
    unsafe fn from_shared(s: &String) -> Self {
        SharingStr(str::from_utf8_unchecked(&*slice_from_raw_parts(s.as_ptr(), s.len())))
    }
}

impl<S: Sharing + Hash + Eq> SchemaRegistry<S> {
    pub fn new() -> Self {
        SchemaRegistry(HashMap::new())
    }

    pub fn register(&mut self, schema: Schema) -> &mut Self {
        // self.0.insert(&schema.name, schema);
        let sharing = unsafe { S::from_shared(&schema.name) };
        {
            // SAFETY: 由于Sharing是共享间接数据,K固定依赖于V存活,所以修改V一定要先修改K,K-V成对处理
            self.0.remove_entry(&sharing);
            self.0.insert(sharing, schema);
        }
        self
    }
}

#[cfg(test)]
mod tests {

    use crate::*;

    #[test]
    fn example() {
        // let mut registry = SchemaRegistry::<SharingString>::new();
        let mut registry = SchemaRegistry::<SharingStr>::new();
        registry.register(Schema {name: "abc".to_owned(), _sections: vec!["first".to_owned()]});
        println!("{:?}", registry.0);
        
        registry.register(Schema {name: "ABC".to_owned(), _sections: vec!["second".to_owned()]});
        println!("{:?}", registry.0);

        registry.register(Schema {name: "abc".to_owned(), _sections: vec!["third".to_owned()]});
        println!("{:?}", registry.0);
    }
}

为了展示两种实现,做了个Sharing特征来方便复用,SharingStringSharingStr是给前面说的方案套了个壳(wrapper)。 具体的实现,可以根据你的功能需求,做一些取舍裁剪,比如上面我在register里面做了通用判断(remove),可以按实际业务优化一下那么一丢丢的少执行点无效代码。

最后,如果K不是String类型的,其实也是类似的,就像Rc这种,外部壳的移动不会涉及实质的数据复制,从而达到减少clone的目的

啥都不是 2024-10-30 18:16
#[derive(Debug, Clone)]
pub struct Schema {
    // name: String,
    sections: Vec<String>,
}

pub struct SchemaRegistry(HashMap<String, Schema>);

impl SchemaRegistry {
    pub fn new() -> Self {
        SchemaRegistry(HashMap::new())
    }

    // pub fn register<'a>(&mut self, schema: Schema) -> &mut Self {
    pub fn register<'a>(&mut self, name: String, schema: Schema) -> &mut Self {
        // self.0.insert(schema.name.clone(), schema);
        self.0.insert(name, schema);
        self
    }
}

代码调整一下,也许可行。

iamazy 2024-10-30 16:56

faststr了解一下

Shylock-Hg 2024-10-30 10:08

直接Arc糊上

asuper 2024-10-30 09:40

我觉得这帖子里的 internment的方案还不错啊

--
👇
TinusgragLin: 太巧了,上个星期 Rust 用户论坛站给我发的一周汇总中就有类似的问题,楼主可以参考一下。

TinusgragLin 2024-10-29 19:52

省流,算hash

我倒觉得不是最好的方案,(原帖里也有讨论到,)如果有碰撞就麻烦了。

Bai-Jinlin 2024-10-29 19:37

省流,算hash

pub struct SchemaRegistry(HashMap<u64, Schema>);

impl SchemaRegistry {
    pub fn new() -> Self {
        SchemaRegistry(HashMap::new())
    }

    pub fn register<'a>(&mut self, schema: Schema) -> &mut Self {
        use std::hash::BuildHasher;
        let k=self.0.hasher().hash_one(&schema.name);
        self.0.insert(k, schema);
        self
    }
}

--
👇
TinusgragLin: 太巧了,上个星期 Rust 用户论坛站给我发的一周汇总中就有类似的问题,楼主可以参考一下。

TinusgragLin 2024-10-29 18:42

太巧了,上个星期 Rust 用户论坛站给我发的一周汇总中就有类似的问题,楼主可以参考一下。

1 共 8 条评论, 1 页