【问题标题】:Rust Deduped Cow HashSetRust Dedued Cow HashSet
【发布时间】:2021-03-02 16:35:12
【问题描述】:

想要存储大量字节数组,并打算使用 AHashMap<Vec<u8>> 来做到这一点,并使用 Cow 从它借用并只在需要时写入,这也应该导致它被删除重复数据。但是到目前为止我的尝试都是徒劳的:

#![feature(hash_set_entry)]

use std::borrow::{Borrow, Cow, ToOwned};
use std::hash::Hash;

use ahash::AHashSet;

#[derive(Debug)]
struct CipherText<'a> {
    ciphertext: Cow<'a, Vec<u8>>,
    ciphers: Vec<Cipher<'a>>,
}

#[derive(Debug)]
struct Cipher<'a> {
    cipher_id: Cow<'a, Vec<u8>>,
    keys: Vec<Cow<'a, Vec<u8>>>,
}

fn main() {
    let mut string_table: AHashSet<Vec<u8>> = vec![
        "Hello World!".as_bytes().to_vec(),
        "atbash".as_bytes().to_vec(),
        "caesar_decrypt".as_bytes().to_vec(),
        "5".as_bytes().to_vec(),
    ]
    .into_iter()
    .collect();

    let mut ciphertexts: Vec<CipherText> = vec![
        CipherText {
            ciphertext: Cow::Borrowed(
                string_table
                    .get(&"Hello World!".as_bytes().to_vec())
                    .unwrap(),
            ),
            ciphers: vec![Cipher {
                cipher_id: Cow::Borrowed(string_table.get(&"atbash".as_bytes().to_vec()).unwrap()),
                keys: vec![],
            }],
        },
        CipherText {
            ciphertext: Cow::Borrowed(
                string_table
                    .get(&"Hello World!".as_bytes().to_vec())
                    .unwrap(),
            ),
            ciphers: vec![Cipher {
                cipher_id: Cow::Borrowed(
                    string_table
                        .get(&"caesar_decrypt".as_bytes().to_vec())
                        .unwrap(),
                ),
                keys: vec![Cow::Borrowed(
                    string_table.get(&"5".as_bytes().to_vec()).unwrap(),
                )],
            }],
        },
    ];

    string_table.insert("TEST".as_bytes().to_vec());
    string_table.insert("TEST2".as_bytes().to_vec());

    ciphertexts[0].ciphertext = Cow::Borrowed(
        &string_table.get_or_insert_owned(&"Goodbye Cruel World...".as_bytes().to_vec()),
    );
}

TEST 行以及 ciphertext[0] 行错误如下

error[E0502]: cannot borrow `string_table` as mutable because it is also borrowed as immutable
  --> src/main.rs:61:5
   |
33 |                 string_table
   |                 ------------ immutable borrow occurs here
...
61 |     string_table.insert("TEST".as_bytes().to_vec());
   |     ^^^^^^^^^^^^ mutable borrow occurs here
...
64 |     ciphertexts[0].ciphertext = Cow::Borrowed(
   |     ----------- immutable borrow later used here

我的目标是让所有字节数组仅作为引用,然后克隆,添加到 string_table 并在我更改它时引用它。此数据将以自定义二进制格式存储,这是编写序列化器和反序列化器过程的开始。希望这一切都有意义!

【问题讨论】:

  • 什么不起作用?您是否有错误或输出不是您所期望的?
  • @PeterHall 编辑了它
  • 只需将两行string_table.insert 移到let mut ciphertexts.. 之前
  • ciphertextsstring_table 借用数据,而您试图在ciphertexts 仍在范围内时改变string_table
  • @PeterHall 字符串表可以更有效地表示字符串何时重复。我希望密文 vec 包含对该 string_table 的引用。当我更改密文 vec 中的值时,如果它存在于字符串表中,我想指向它,否则我想在字符串表中创建它并指向它。这将通过 Cow 来完成,以提高效率。抱歉,如果解释得不好

标签: performance rust binaryfiles


【解决方案1】:

CipherText 持有对它的引用时,您不能改变string_table。这只是 Rust 的核心不变量之一。所以你不能使用Cow。绕过它的典型方法通常是:

  1. 使用索引/键来引用其他结构,不幸的是,在这种情况下我看不到这样做的好方法。

  2. 使用Rcs,以便密文和表共享字节的所有权。这确实意味着密文可以独立存在,但您始终可以使用表作为代理来删除元素,然后再明智地使用 get_or_insert

    #![feature(hash_set_entry)]
    
    use std::rc::Rc;
    use ahash::AHashSet;
    
    #[derive(Debug)]
    struct CipherText {
        ciphertext: Rc<Vec<u8>>,
        ciphers: Vec<Cipher>,
    }
    
    #[derive(Debug)]
    struct Cipher {
        cipher_id: Rc<Vec<u8>>,
        keys: Vec<Rc<Vec<u8>>>,
    }
    
    fn main() {
        let mut string_table: AHashSet<Rc<Vec<u8>>> = vec![
            Rc::new("Hello World!".as_bytes().to_vec()),
            Rc::new("atbash".as_bytes().to_vec()),
            Rc::new("caesar_decrypt".as_bytes().to_vec()),
            Rc::new("5".as_bytes().to_vec()),
        ]
        .into_iter()
        .collect();
    
        let mut ciphertexts: Vec<CipherText> = vec![
            CipherText {
                ciphertext: string_table
                    .get_or_insert(Rc::new("Hello World!".as_bytes().to_vec()))
                    .clone(),
                ciphers: vec![Cipher {
                    cipher_id: string_table
                        .get_or_insert(Rc::new("atbash".as_bytes().to_vec()))
                        .clone(),
                    keys: vec![],
                }],
            },
            CipherText {
                ciphertext: string_table
                    .get_or_insert(Rc::new("Hello World!".as_bytes().to_vec()))
                    .clone(),
                ciphers: vec![Cipher {
                    cipher_id: string_table
                        .get_or_insert(Rc::new("caesar_decrypt".as_bytes().to_vec()))
                        .clone(),
                    keys: vec![string_table
                        .get_or_insert(Rc::new("5".as_bytes().to_vec()))
                        .clone()],
                }],
            },
        ];
    
        string_table.insert(Rc::new("TEST".as_bytes().to_vec()));
        string_table.insert(Rc::new("TEST2".as_bytes().to_vec()));
    
        ciphertexts[0].ciphertext = string_table
            .get_or_insert(Rc::new("Goodbye Cruel World...".as_bytes().to_vec()))
            .clone();
    }
    

    这将具有与Cow 相同的效果,Rcs 拥有的元素是不可变的,因此必须创建一个新元素才能进行更改。

【讨论】:

  • 非常感谢您的回复,这对您有很大帮助!只是想知道如果没有被引用,是否/如何从 string_table 中删除项目?
  • 如果Rcstrong_count1,则您可以看出它不会在其他地方使用。
猜你喜欢
  • 2017-02-09
  • 1970-01-01
  • 2016-05-28
  • 2021-12-18
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 2021-09-28
相关资源
最近更新 更多