集合中的 Rust 返回对象,其生存期短于集合本身

Rust return object in collection with lifetime shorter than the collection itself

提问人:Noah 提问时间:10/24/2023 最后编辑:Noah 更新时间:10/24/2023 访问量:47

问:

我正在尝试使用.这个想法是,我有一个包装字节引用的结构,在创建新对象时,我检查它包装的字节是否已经在使用中,如果是,则重用现有的堆分配字节。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 storebw0bw1

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

操场

我认为问题在于,在返回的生命周期中,与传入存储的生命周期相同,但我无法弄清楚如何获取它,以便我只能返回新的生命周期,而不要求它像 (只有 req 持续时间不超过 )。BytesWrapperT::newBytesWrapperT<'a>store_: &'a mut ObjStorageTByteWrapperTObjStorageTObjStorageT

我尝试将实现更改为:

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. 弄清楚如何配置我可以重用的生存期 s.t 和&mut store
  2. 为什么静态版本可以工作

将不胜感激。

谢谢!

编辑: 原谅样式警告:(

Rust 生命周期 借用检查器

评论

0赞 Chayim Friedman 10/24/2023
(无关,但)请问您为什么使用for函数而不是标准?camelCasesnake_case
0赞 Noah 10/24/2023
没有理由。就像所有人个人保持一致一样。
0赞 Chayim Friedman 10/24/2023
然后至少允许棉绒。
1赞 Chayim Friedman 10/24/2023
提示:implements .Box<[u8]>From<&[u8]>
2赞 Chayim Friedman 10/24/2023
关于风格,是的,你可以选择任何你想要的风格,但我强烈建议你自己训练自己到官方的 Rust 风格。否则,其他人将很难阅读您的代码。

答:

1赞 Chayim Friedman 10/24/2023 #1

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

而不是 ,只是存储 (或 )。每次分配只有几个字节,而 refcount 增量则多几个周期,但它可以让您安全地完成所有操作: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);
    }
}

但是,无法更改插入的字节。我认为这是一个很好的属性:它们是共享的,你不希望有人在你背后更改你的字节。如果你真的愿意,你可以把它们包装在一个 或 .MutexRwLock

评论

0赞 Noah 10/24/2023
谢谢。认为这可能是最有道理的,但你有机会向我指出解决终身问题的不安全技术吗?
1赞 Chayim Friedman 10/24/2023
@Noah 如果你是 Rust 的初学者,你不需要它。如果你不知道你是否需要它,你就不需要它。如果你需要它,你就会知道。如果你只想学习,神奇的诀窍就是让。它类似于 arena,只是大多数 arena 实现在分配上产生,并且从不重用相同的分配,并且您希望 yo yield()(否则重用是不合理的)并在现有记录中搜索。getBytes()&self&mut T&T&[u8]
0赞 Noah 10/24/2023
很公平。为什么静态版本有效,而参数 1 不起作用?似乎两者都应该遭受同样的问题,即持有对一家比谁更久的商店的引用?
0赞 Chayim Friedman 10/24/2023
这两个版本是什么意思?
0赞 Noah 10/24/2023
此版本能够编译。它基本上是相同的代码,但它没有使用函数的本地代码,而是使用可变的全局静态。storestore