我希望有一个名为Factory的struct,它动态地生成新的Strings,将它们保存在自己内部,并返回&str个与Factory Value本身相同生命周期 的borrow .

我试图将新值保持在Vec以内,但随着Vec的增长,对元素的borrow 将失效,因此它们的生命周期 不够长.我试着用BoxesRefCells包装它们,但我遇到了同样的问题.

我还想在循环中调用这个工厂方法,这样我就可以在每次迭代中生成新的字符串,并borrow 它来保存在某个地方.


有一个 crate ,叫串口:https://docs.rs/string-interner/latest/string_interner/index.html

如果您想要的只是字符串句柄,那么直接使用它或通过类似的 struct 使用它可能是一个好主意,如下所示.


这就是我到目前为止得到的信息,感谢你们的 comments :

use std::{ cell::{Ref, RefCell}, rc::Rc, }; 

struct StringHandle {
    key: usize,
    store: Rc<RefCell<Vec<String>>>,
}
impl StringHandle {
    pub fn get(&self) -> Ref<String> {
        Ref::map(self.store.borrow(), |v| &v[self.key])
    }
}

struct Factory {
    pub store: Rc<RefCell<Vec<String>>>,
}
impl Factory {
    pub fn make_next_string(&mut self) -> StringHandle {
        let len = self.store.borrow().len();
        self.store.borrow_mut().push(format!("string no. {}", len));
        StringHandle {
            store: self.store.clone(),
            key: len,
        }
    }
    pub fn new() -> Factory {
        Factory { store: Rc::new(RefCell::new(vec![])) }
    }
}

let mut f = Factory::new();
let mut strs: Vec<StringHandle> = vec![];

for _ in 0..5 {
    let handle = f.make_next_string();
    strs.push(handle);
}

for handle in strs {
    println!("{}", handle.get());
}

字符串以外的 struct 的泛型版本:

use std::{ cell::{Ref, RefCell, RefMut}, rc::Rc, }; 

struct Handle<T> {
    key: usize,
    store: Rc<RefCell<Vec<T>>>,
}
impl<T> Handle<T> {
    pub fn get(&self) -> Ref<T> {
        Ref::map(self.store.borrow(), |v| &v[self.key])
    }
    pub fn get_mut(&self) -> RefMut<T> {
        RefMut::map(self.store.borrow_mut(), |v| &mut v[self.key])
    }
}

struct Factory<T> {
    pub store: Rc<RefCell<Vec<T>>>,
}
impl<T: Default> Factory<T> {
    pub fn make_next(&mut self) -> Handle<T> {
        let len = self.store.borrow().len();
        self.store.borrow_mut().push(T::default());
        Handle {
            store: self.store.clone(),
            key: len,
        }
    }
    pub fn new() -> Factory<T> {
        Factory { store: Rc::new(RefCell::new(vec![])) }
    }

}

#[derive(Debug)]
struct Data {
    pub number: i32
}

impl Default for Data {
    fn default() -> Self {
        Data { number: 0 }
    }
}

let mut objs: Vec<Handle<Data>> = vec![];
let mut f: Factory<Data> = Factory::new();



for i in 0..5 {
    let handle = f.make_next();

    handle.get_mut().number = i;

    objs.push(handle);
}

for handle in objs {
    println!("{:?}", handle.get());
}

推荐答案

首先,如果你对实习生有&mut%的访问权,你就不需要RefCell.但您可能希望通过共享引用访问它,因此您确实需要.

另一种方法是将新类型的索引返回到Vec位,而不是引用.这节省了间接性,但需要访问Interner才能访问Intered字符串,因此它可能无法满足要求.这也不允许您在保留对旧字符串的引用时分配新字符串(使用RefCell无济于事,它只会引发panic ):

use std::ops::Index;

struct StringHandle(usize);

struct Factory {
    pub store: Vec<String>,
}
impl Factory {
    pub fn make_next_string(&mut self) -> StringHandle {
        let len = self.store.len();
        self.store.push(format!("string no. {}", len));
        StringHandle(len)
    }
    pub fn new() -> Factory {
        Factory { store: vec![] }
    }
}

impl Index<StringHandle> for Factory {
    type Output = str;
    fn index(&self, index: StringHandle) -> &Self::Output {
        &self.store[index.0]
    }
}

fn main() {
    let mut f = Factory::new();
    let mut strs: Vec<StringHandle> = vec![];

    for _ in 0..5 {
        let handle = f.make_next_string();
        strs.push(handle);
    }

    for handle in strs {
        println!("{}", &f[handle]);
    }
}

最好的办法是用一张arena.它允许您产生引用(因此不需要访问Interner来访问Interactive字符串),并在创建新字符串的同时保留旧的字符串.缺点是它需要使用 crate ,因为您可能不想自己实现竞技场(这也需要不安全的代码),并且您不能将该Interner与被套住的字符串一起存储(这是一个自引用 struct ).您可以使用typed-arena crate :

use std::cell::Cell;

use typed_arena::Arena;

struct Factory {
    store: Arena<String>,
    len: Cell<u32>,
}

impl Factory {
    pub fn make_next_string(&self) -> &str {
        let len = self.len.get();
        self.len.set(len + 1);
        self.store.alloc(format!("string no. {}", len))
    }
    pub fn new() -> Factory {
        Factory { store: Arena::new(), len: Cell::new(0) }
    }
}

fn main() {
    let f = Factory::new();
    let mut strs: Vec<&str> = vec![];

    for _ in 0..5 {
        let interned = f.make_next_string();
        strs.push(interned);
    }

    for interned in strs {
        println!("{}", interned);
    }
}

还可以将str存储在区域内(而不是String).优点是更好的高速缓存访问,因为 struct 更扁平,并且由于不需要循环和丢弃存储的字符串,内部本身的下降速度要快得多;缺点是您需要在存储字符串之前复制它们.我推荐bumpalo条:

use std::cell::Cell;

use bumpalo::Bump;

struct Factory {
    store: Bump,
    len: Cell<u32>,
}

impl Factory {
    pub fn make_next_string(&self) -> &str {
        let len = self.len.get();
        self.len.set(len + 1);
        self.store.alloc_str(&format!("string no. {}", len))
    }
    pub fn new() -> Factory {
        Factory { store: Bump::new(), len: Cell::new(0) }
    }
}

fn main() {
    let f = Factory::new();
    let mut strs: Vec<&str> = vec![];

    for _ in 0..5 {
        let interned = f.make_next_string();
        strs.push(interned);
    }

    for interned in strs {
        println!("{}", interned);
    }
}

Rust相关问答推荐

通用池类型xsx

Rust中的相互递归特性与默认实现

如何使用字符串迭代器执行查找?

如何创建引用构造函数拥有的变量的对象?

展开枚举变量并返回所属值或引用

铁 rust ,我的模块介绍突然遇到了一个问题

在文件链实施中绕过borrow 判断器

Tokio';s io::用Cursor拆分<;Vec<;u8>>;赢得';t get the full writted data

Windows 上 ndarray-linalg 与 mkl-stats 的链接时间错误

为什么我必须使用 PhantomData?在这种情况下它在做什么?

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

为什么切片时需要参考?

更新 rust ndarray 中矩阵的一行

Google chrome 和 Apple M1 中的计算着色器

闭包返回类型的生命周期规范

Rust 异步循环函数阻塞了其他future 任务的执行

Rust 编译器不统一在 if let 表达式的分支中都 impl Future 的类型

如何从 x86_64 Mac 构建 M1 Mac?

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

有没有办法阻止 rust-analyzer 使非活动代码变暗?