我如何创建一个Box,将它放在一个向量中,并将对它的可变引用保存在一个结构中



我正在构建一个基于流的文件解析器。该文件的格式为$TYPE$CONTENTn,其中TYPE为4字节,content最多为\n。

$TYPE要么是当前记录的属性,要么指示新记录开始。例如:

strtPerson
nameMustermann
surnMax
strtPerson
nameMustermann
surnRenate

我的方法是打开文件,在行上获得迭代器,分割行,然后将行提供给解析器:

use std::io::{self, BufRead, BufReader};
pub trait Record {
fn add_line(&mut self, type_: String, content: String) -> &mut dyn Record;
}
pub struct File {
pub current_record: Option<Box<dyn Record>>,
pub records: Vec<Box<dyn Record>>,
}
impl File {
pub fn push_last_record(&mut self) {
if self.current_record.is_some() {
self.records.push(self.current_record.take().unwrap());
}
}
}
impl Record for File {
fn add_line(&mut self, type_: String, content: String) -> &mut dyn Record {
match &type_[..] {
"strt" => {
self.push_last_record();
self.current_record = Some(Box::new(File {
current_record: None,
records: Vec::new(),
}));
}
_ => {
if let Some(current) = &mut self.current_record {
current.add_line(type_, content);
}
}
}
self
}
}
fn main() -> io::Result<()> {
let f =
"strtPersonnnameMustermannnsurnMaxnstrtPersonnnameMustermannnsurnRenate".as_bytes();
let reader = BufReader::new(f);
let mut f = File {
current_record: None,
records: Vec::new(),
};
for line in reader.lines() {
let line = line.unwrap();
f.add_line(line[..4].to_owned(), line[5..].to_owned());
}
println!("there are {} records read", f.records.len());
f.push_last_record();
println!("there are now {} records read", f.records.len());
Ok(())
}

这个解决方案是有效的,但我认为需要调用push_last_record会很麻烦,而且容易出错,我认为可能有一个更惯用的解决方案。

我的主要问题是,如果行以"0"开头,如何创建新的CCD_;"开始";并以某种方式保存对它的可变引用,这样我就可以直接调用最新记录上的add_line函数。

它可以归结为创建一个Box,将它放在向量中,并在结构中保存对它的可变引用。

有现成的模式吗?或者有人能给我一个如何清洁的提示吗?

编辑:

由于last_mut()的提示,我重写了代码。我不确定它是地道的还是有效的,但我认为它比以前干净多了。

use std::io::{self, BufRead, BufReader};
pub trait Record {
fn add_line(&mut self, type_: String, content: String) -> &mut dyn Record;
}
pub struct File<'a> {
pub current_record: Option<&'a mut Box<dyn Record>>,
pub records: Vec<Box<dyn Record>>,
}
impl Record for File<'_> {
fn add_line(&mut self, type_: String, content: String) -> &mut dyn Record {
match &type_[..] {
"strt" => {
self.records.push(Box::new(File {
current_record: None,
records: Vec::new(),
}));
}
_ => {
if let Some(current) = &mut self.records.last_mut() {
current.add_line(type_, content);
}
}
}
self
}
}
fn main() -> io::Result<()> {
let f =
"strtPersonnnameMustermannnsurnMaxnstrtPersonnnameMustermannnsurnRenate".as_bytes();
let reader = BufReader::new(f);
let mut f = File {
current_record: None,
records: Vec::new(),
};
for line in reader.lines() {
let line = line.unwrap();
f.add_line(line[..4].to_owned(), line[5..].to_owned());
}
println!("there are {} records read", f.records.len());
Ok(())
}

对于current_record,您应该将索引存储在另一个Vec中,而不是使用临时引用。自引用结构在Rust中是不安全的,即使它们有效,结构中的临时引用也是非常有限和不切实际的。

或者,您可以使用Arc而不是Box,以便允许一个值居住在多个位置。

最新更新