提问人:Kamil Szot 提问时间:12/22/2022 最后编辑:Kamil Szot 更新时间:12/22/2022 访问量:79
如何创建动态创造价值并回报借款的工厂?
How to create factory that dynamically creates values and returns borrows to them?
问:
我想要一个叫做 Factory 的工厂,它可以动态地生产新的,将它们保留在自己内部并返回借用的它们,这些借款的寿命与工厂本身的价值一样长。struct
Strings
&str
我试图将新值保留在里面,但随着增长,对元素的借用会失效,因此它们的寿命不够长。我试着把它们包起来,但我遇到了同样的问题。Vec
Vec
Boxes
RefCells
我还想在循环中调用这个工厂方法,这样我就可以在每次迭代中创建新的 String 并借用它以保存在某个地方。
有一个叫做string-interner的箱子:https://docs.rs/string-interner/latest/string_interner/index.html
如果您想要的只是 String 句柄,那么直接或通过类似的结构使用它可能是一个好主意,如下所示。
这就是我到目前为止所得到的,这要归功于您的评论:
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());
}
以及 String 以外的结构的通用版本:
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());
}
答:
首先,如果您可以访问 interner,则不需要它。但是您可能希望通过共享引用来访问它,因此您确实需要。&mut
RefCell
另一种方法是将新类型索引返回到 而不是引用中。这节省了间接内容,但需要访问中间人才能访问被拘禁的字符串,因此它可能不满足要求。这也不允许你在保留对旧字符串的引用时分配新字符串(使用无济于事,它只会恐慌):Vec
RefCell
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]);
}
}
最好的方法是使用竞技场。它允许您生成引用(因此不需要访问内部器即可访问被拘禁的字符串),并在创建新字符串时保留旧的。缺点是它需要使用 crate,因为您可能不想自己实现 arena(这也需要不安全的代码),并且您不能将该 interner 存储在被拘禁的字符串旁边(这是一个自引用结构)。您可以使用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);
}
}
您还可以将 s 存储在 arean 中(而不是 s) 优点是更好的缓存访问,因为结构更扁平,并且由于不需要循环和丢弃存储的字符串,interner 本身的删除速度要快得多;缺点是您需要在存储字符串之前复制它们。我推荐 bumpalo
:str
String
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);
}
}
评论
像pinus
这样的东西