Rust 返回集合中的对象,其生命周期比集合本身短

问题描述 投票:0回答:1

我正在尝试使用

HashSet
创建堆分配对象的缓存。我的想法是,我有一个包装字节引用的结构,在创建新对象时,我检查它包装的字节是否已在使用中,如果是,则重新使用现有的堆分配的字节。

#![feature(new_uninit)]

#[derive(Debug, Clone)]
pub struct ObjStorageT {
    /// Tab for unique storage
    pub tab_: std::collections::HashSet<Box<[u8]>>,
}

impl ObjStorageT {
    /// Create new byte storage
    pub fn new() -> Self {
        return ObjStorageT {
            tab_: std::collections::HashSet::<Box<[u8]>>::new(),
        };
    }

    /// Get existing bytes from storage or new new bytes if they don't exist.
    pub fn getBytes(&mut self, bytes: &[u8]) -> &[u8] {
        if !self.tab_.contains(bytes) {
            let mut new_bytes_base: Box<[std::mem::MaybeUninit<u8>]> =
                Box::new_uninit_slice(bytes.len());
            for i in 0..bytes.len() {
                new_bytes_base[i].write(bytes[i]);
            }
            self.tab_.insert(unsafe { new_bytes_base.assume_init() });
        }
        let existing_bytes: Option<&Box<[u8]>> = self.tab_.get(bytes);
        return existing_bytes.unwrap();
    }
}

/// Get storage for ir constants
pub fn globalStorage() -> &'static mut ObjStorageT {
    static mut GLBStorageOnce: bool = false;
    static mut GLBStorage: std::mem::MaybeUninit<ObjStorageT> = std::mem::MaybeUninit::uninit();
    #[allow(unsafe_code)]
    unsafe {
        if !GLBStorageOnce {
            GLBStorageOnce = true;
            GLBStorage.write(ObjStorageT::new());
        }
        return GLBStorage.assume_init_mut();
    }
}

#[derive(Debug, Copy, Clone)]
pub struct BytesWrapperT<'a> {
    bytes_: &'a [u8],
}

impl BytesWrapperT<'_> {
    /// New new constant w/ global obj storage
    pub fn new<'a, 'b>(store_: &'a mut ObjStorageT, bytes: &[u8]) -> BytesWrapperT<'a> {
        let new_bytes = store_.getBytes(bytes);
        return BytesWrapperT { bytes_: new_bytes };
    }

    /// New new constant w/ global obj storage
    pub fn newGlobal<'g>(bytes: &[u8]) -> BytesWrapperT<'g> {
        let new_bytes = globalStorage().getBytes(bytes);
        return BytesWrapperT { bytes_: new_bytes };
    }

    pub fn isZeros(self) -> bool {
        return self.bytes_.into_iter().all(|x| *x == 0);
    }
}

// Doesn't Compile
fn main() {
    let mut store: ObjStorageT = ObjStorageT::new();
    let mut arr0: [u8; 64] = [0; 64];

    let bw0 = BytesWrapperT::new(&mut store, &arr0);
    assert!(bw0.isZeros());

    arr0[0] = 1;
    let bw1 = BytesWrapperT::new(&mut store, &arr0);
    assert!(!bw1.isZeros());
    assert!(bw0.isZeros());
}

问题是这段代码无法编译,因为

&mut store
bw0
bw1
借用,即:

74 |     let bw0 = BytesWrapperT::new(&mut store, &arr0);
   |                                  ---------- first mutable borrow occurs here
...
78 |     let bw1 = BytesWrapperT::new(&mut store, &arr0);
   |                                  ^^^^^^^^^^ second mutable borrow occurs here
79 |     assert!(!bw1.isZeros());
80 |     assert!(bw0.isZeros());
   |             --- first borrow later used here

游乐场

我认为问题在于,在

BytesWrapperT::new
中,
BytesWrapperT<'a>
返回的生命周期与传入商店
store_: &'a mut ObjStorageT
的生命周期相同,但我不知道如何获取它,以便我只能返回生命周期与新的
ByteWrapperT
一样长,而不要求它与
ObjStorageT
一样长(仅要求的持续时间不长于
ObjStorageT
)。

我尝试将实现更改为:

impl<'a, 'b> BytesWrapperT<'b> {
    /// New new constant w/ global obj storage
    pub fn new(store_: &'a mut ObjStorageT, bytes: &[u8]) -> BytesWrapperT<'b> {
        let new_bytes = store_.getBytes(bytes);
        return BytesWrapperT { bytes_: new_bytes };
    }
// ...
}

Playground 但这不起作用,因为

ObjStorageT::getBytes
的返回生命周期不匹配。

我能够使用全局静态变量来编译代码(请参阅Playground),但说实话,从生命周期的角度来看,我真的不明白那可以做什么,但采用

&mut store_
参数的那个却不能

任何帮助

  1. 弄清楚如何配置生命周期,我可以重用
    &mut store
  2. 为什么静态版本完全有效

将不胜感激。

谢谢!

编辑: 请原谅风格警告:(

rust lifetime borrow-checker
1个回答
0
投票

可以使用不安全的代码做您想做的事情,但您很可能不需要它。您可以选择性能几乎相同的安全等效产品。

而不是

Box<[u8]>
,只需存储
Arc<[u8]>
(或
Rc<[u8]>
)。每次分配只需要几个字节,而引用计数增量则需要更多的周期,但它可以让您安全地完成这一切:

use std::collections::HashSet;
use std::sync::Arc;

#[derive(Debug, Clone)]
pub struct ObjStorageT {
    /// Tab for unique storage
    pub tab_: HashSet<Arc<[u8]>>,
}

impl ObjStorageT {
    /// Create new byte storage
    pub fn new() -> Self {
        return ObjStorageT {
            tab_: HashSet::new(),
        };
    }

    /// Get existing bytes from storage or new new bytes if they don't exist.
    pub fn getBytes(&mut self, bytes: &[u8]) -> Arc<[u8]> {
        match self.tab_.get(bytes) {
            Some(existing_bytes) => Arc::clone(existing_bytes),
            None => {
                let bytes = bytes.into();
                self.tab_.insert(Arc::clone(&bytes));
                bytes
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct BytesWrapperT {
    bytes_: Arc<[u8]>,
}

impl BytesWrapperT {
    /// New new constant w/ global obj storage
    pub fn new(store_: &mut ObjStorageT, bytes: &[u8]) -> BytesWrapperT {
        let new_bytes = store_.getBytes(bytes);
        return BytesWrapperT { bytes_: new_bytes };
    }

    /// New new constant w/ global obj storage
    pub fn newGlobal(bytes: &[u8]) -> BytesWrapperT {
        let new_bytes = globalStorage().getBytes(bytes);
        return BytesWrapperT { bytes_: new_bytes };
    }

    pub fn isZeros(self) -> bool {
        return self.bytes_.into_iter().all(|x| *x == 0);
    }
}

但是,无法更改插入的字节。我认为这是一个很好的属性:它们是共享的,并且您不希望有人在您背后更改您的字节。如果您确实愿意,可以将它们包裹在

Mutex
RwLock
中。

© www.soinside.com 2019 - 2024. All rights reserved.