Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用哈希集规范化Rust中的对象_Rust_Hashset_Canonicalization - Fatal编程技术网

使用哈希集规范化Rust中的对象

使用哈希集规范化Rust中的对象,rust,hashset,canonicalization,Rust,Hashset,Canonicalization,作为一项教育活动,我正在考虑移植到Rust 它的基本操作模式是将大量CVS主文件解析为中间形式,然后分析中间形式,目的是将其转换为git快速导出流 解析时要做的一件事是将中间形式的公共部分转换为规范表示。一个激励性的例子是提交作者。CVS存储库可能有数十万个单独的文件提交,但可能不到一千个作者。因此,在解析时使用一个interning表,当您从文件解析作者时输入作者,它将给您一个指向规范版本的指针,如果它以前没有看到过,则创建一个新版本。(我也听说过这叫做雾化或实习)。然后,该指针存储在中间对象

作为一项教育活动,我正在考虑移植到Rust

它的基本操作模式是将大量CVS主文件解析为中间形式,然后分析中间形式,目的是将其转换为git快速导出流

解析时要做的一件事是将中间形式的公共部分转换为规范表示。一个激励性的例子是提交作者。CVS存储库可能有数十万个单独的文件提交,但可能不到一千个作者。因此,在解析时使用一个interning表,当您从文件解析作者时输入作者,它将给您一个指向规范版本的指针,如果它以前没有看到过,则创建一个新版本。(我也听说过这叫做雾化或实习)。然后,该指针存储在中间对象上

在Rust中,我第一次尝试做类似的事情,尝试使用
HashSet
作为interning表。注意:这使用的是CVS版本号而不是作者,这只是一个数字序列,如1.2.3.4,表示为
Vec

use std::collections::HashSet;
use std::hash::Hash;

#[derive(PartialEq, Eq, Debug, Hash, Clone)]
struct CvsNumber(Vec<u16>);

fn intern<T:Eq + Hash + Clone>(set: &mut HashSet<T>, item: T) -> &T {
    let dupe = item.clone();
    if !set.contains(&item) {
        set.insert(item);
    }
    set.get(&dupe).unwrap()
}

fn main() {
    let mut set: HashSet<CvsNumber> = HashSet::new();
    let c1 = CvsNumber(vec![1, 2]);
    let c2 = intern(&mut set, c1);
    let c3 = CvsNumber(vec![1, 2]);
    let c4 = intern(&mut set, c3);
}
使用std::collections::HashSet;
使用std::hash::hash;
#[派生(PartialEq、Eq、调试、哈希、克隆)]
结构CvsNumber(Vec);
fn实习生(集合:&mut哈希集合,项目:T)->&T{
让dupe=item.clone();
if!set.contains(&item){
套。插入(项目);
}
set.get(&dupe).unwrap()
}
fn main(){
让mut set:HashSet=HashSet::new();
设c1=CvsNumber(vec![1,2]);
设c2=内部(和多个集合,c1);
设c3=CvsNumber(vec![1,2]);
设c4=内部(和多个集合,c3);
}
此操作失败,出现
错误[E0499]:一次不能将“set”作为可变项借用多次
。这很公平,
HashSet
不保证在获得引用后添加更多项时对其键的引用有效。C版本谨慎地保证了这一点。为了得到这个保证,我认为
哈希集
应该在
上方。然而,我无法向借书人解释它的使用寿命

这里我要介绍的所有权模型是interning表拥有数据的规范版本,并分发引用。只要interning表存在,引用就应该有效。我们应该能够在不使旧引用无效的情况下向interning表添加新内容。我认为我的问题的根源在于,我不知道如何以与Rust所有权模型一致的方式编写此合同的接口

我在有限的防锈知识中看到的解决方案有:

  • 执行两个过程,在第一个过程中构建一个
    HashSet
    ,然后在第二个过程中冻结它并使用引用。这意味着额外的临时存储(有时是大量存储)
  • 不安全

  • 有人有更好的想法吗?

    你的分析是正确的。最终的问题是,当修改
    HashSet
    时,编译器无法保证突变不会影响现有的分配。事实上,在一般情况下,它们可能会影响它们,除非您添加另一层间接寻址,正如您已经确定的那样

    这是
    不安全
    非常有用的地方的一个典型例子。作为程序员,您可以断言代码只能以特定的方式使用,并且这种特定的方式将允许变量通过任何突变保持稳定。可以使用类型系统和模块可见性来帮助强制执行这些条件

    请注意,
    String
    已经引入了堆分配。只要分配后不更改
    字符串
    ,就不需要额外的

    像这样的事情似乎是一个好的开始:

    use std::{cell::RefCell, collections::HashSet, mem};
    
    struct EasyInterner(RefCell<HashSet<String>>);
    
    impl EasyInterner {
        fn new() -> Self {
            EasyInterner(RefCell::new(HashSet::new()))
        }
    
        fn intern<'a>(&'a self, s: &str) -> &'a str {
            let mut set = self.0.borrow_mut();
    
            if !set.contains(s) {
                set.insert(s.into());
            }
    
            let interned = set.get(s).expect("Impossible missing string");
    
            // TODO: Document the pre- and post-conditions that the code must
            // uphold to make this unsafe code valid instead of copying this
            // from Stack Overflow without reading it
            unsafe { mem::transmute(interned.as_str()) }
        }
    }
    
    fn main() {
        let i = EasyInterner::new();
    
        let a = i.intern("hello");
        let b = i.intern("world");
        let c = i.intern("hello");
    
        // Still strings
        assert_eq!(a, "hello");
        assert_eq!(a, c);
        assert_eq!(b, "world");
    
        // But with the same address
        assert_eq!(a.as_ptr(), c.as_ptr());
        assert!(a.as_ptr() != b.as_ptr());
    
        // This shouldn't compile; a cannot outlive the interner
        // let x = {
        //     let i = EasyInterner::new();
        //     let a = i.intern("hello");
        //     a
        // };
    
        let the_pointer;
        let i = {
            let i = EasyInterner::new();
            {
                // Introduce a scope to contstrain the borrow of `i` for `s`
                let s = i.intern("inner");
                the_pointer = s.as_ptr();
            }
            i // moving i to a new location
              // All outstanding borrows are invalidated
        };
    
        // but the data is still allocated
        let s = i.intern("inner");
        assert_eq!(the_pointer, s.as_ptr());
    }
    
    使用std:{cell::RefCell,collections::HashSet,mem};
    结构EasyInterner(RefCell);
    简单易懂{
    fn new()->Self{
    EasyInterner(RefCell::new(HashSet::new())
    }
    fn实习生和a街{
    设mut set=self.0.borrow_mut();
    if!set.contains(个){
    set.插入(s.到());
    }
    让interned=set.get.expect(“不可能缺少字符串”);
    //TODO:记录代码必须满足的前置和后置条件
    //坚持使此不安全代码有效,而不是复制此代码
    //从堆栈溢出而不读取它
    不安全{mem::transmute(interned.as_str())}
    }
    }
    fn main(){
    设i=EasyInterner::new();
    让a=i.intern(“你好”);
    让b=i.实习生(“世界”);
    让c=i.intern(“你好”);
    //静止弦
    断言(a,“你好”);
    断言(a,c);
    断言(b,“世界”);
    //但地址是一样的
    断言(a.as_ptr(),c.as_ptr());
    断言!(a.as_ptr()!=b.as_ptr());
    //这不应该编译;一个不能比实习生活得更长
    //设x={
    //设i=EasyInterner::new();
    //让a=i.intern(“你好”);
    //a
    // };
    让_指针;
    让我={
    设i=EasyInterner::new();
    {
    //引入一个作用域来扩展'i'for'的借用`
    设s=i.intern(“内部”);
    _指针=s.as_ptr();
    }
    i//将i移动到新位置
    //所有未偿还的借款均无效
    };
    //但数据仍在分配中
    设s=i.intern(“内部”);
    断言(指针,s.as_ptr());
    }
    
    但是,使用板条箱可能更为方便,如:

    • ,它拥有伺服项目的集体智慧

    我有点不同意@Shepmaster在这里使用不安全的

    虽然现在它不会引起问题,但如果将来有人决定更改
    HashSet
    的使用以包括一些删减(例如,只保留一百个作者),那么
    不安全的
    将严厉地打击你

    在没有str的情况下
    use std::collections::HashSet;
    use std::hash::Hash;
    use std::rc::Rc;
    
    #[derive(PartialEq, Eq, Debug, Hash, Clone)]
    struct CvsNumber(Rc<Vec<u16>>);
    
    fn intern<T:Eq + Hash + Clone>(set: &mut HashSet<T>, item: T) -> T {
        if !set.contains(&item) {
            let dupe = item.clone();
            set.insert(dupe);
            item
        } else {
            set.get(&item).unwrap().clone()
        }
    }
    
    fn main() {
        let mut set: HashSet<CvsNumber> = HashSet::new();
        let c1 = CvsNumber(Rc::new(vec![1, 2]));
        let c2 = intern(&mut set, c1);
        let c3 = CvsNumber(Rc::new(vec![1, 2]));
        let c4 = intern(&mut set, c3);
    }