gpt4 book ai didi

memory-management - 我可以通过持有引用来避免克隆字符串吗?

转载 作者:行者123 更新时间:2023-11-29 08:26:49 25 4
gpt4 key购买 nike

我有一个数据结构,我在其中提供了一个围绕读取缓冲区的包装器,以自动处理读出中的重复语句。

这是通过存储剩余重复次数和要重复的行的内部状态来完成的。

use std::fs::File;
use std::path::Path;
use std::io::BufReader;
use std::io::prelude::*;
use std::io::Error;
use std::num::NonZeroU32;
use std::mem;

pub struct Reader {
handle: BufReader<File>,
repeat_state: Option<(NonZeroU32, String)>,
}

impl Reader {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Reader, Error> {
let file = File::open(path)?;
let handle = BufReader::new(file);

Ok(Reader {
handle,
repeat_state: None,
})
}

/// get next line, respecting repeat instructions
pub fn next_line(&mut self) -> Option<String> {
if self.repeat_state.is_some() {
let (repeats_left, last_line) = mem::replace(&mut self.repeat_state, None).unwrap();

self.repeat_state = NonZeroU32::new(repeats_left.get() - 1)
.map(|repeats_left| (repeats_left, last_line.clone()));

Some(last_line)
} else {
let mut line = String::new();
if self.handle.read_line(&mut line).is_err() || line.is_empty() {
return None
}

if line.starts_with("repeat ") {
let repeats: Option<u32> = line.chars().skip(7)
.take_while(|c| c.is_numeric())
.collect::<String>().parse().ok();

self.repeat_state = repeats
.and_then(|repeats| NonZeroU32::new(repeats - 1))
.map(|repeats_left| (repeats_left, line.clone()))
}

Some(line)
}
}
}

#[test]
fn test_next_line() {
let source = "
line one
repeat 2 line two and line three
line four
repeat 11 lines 5-15
line 16
line 17
last line (18)
".trim();
let mut input = File::create("file.txt").unwrap();
write!(input, "{}", source);


let mut read = Reader::new("file.txt").unwrap();
assert_eq!(
read.next_line(),
Some("line one\n".to_string())
);
assert_eq!(
read.next_line(),
Some("repeat 2 line two and line three\n".to_string())
);
assert_eq!(
read.next_line(),
Some("repeat 2 line two and line three\n".to_string())
);
assert_eq!(
read.next_line(),
Some("line four\n".to_string())
);

for _ in 5..=15 {
assert_eq!(
read.next_line(),
Some("repeat 11 lines 5-15\n".to_string())
);
}

assert_eq!(
read.next_line(),
Some("line 16\n".to_string())
);
assert_eq!(
read.next_line(),
Some("line 17\n".to_string())
);
assert_eq!(
read.next_line(),
Some("last line (18)".to_string())
);
}

Playground

问题是我每次都必须克隆保留的重复值,以便保留它并返回它。我想通过返回(并可能存储)一个 &str 来避免这些昂贵的克隆.我已经尝试了几件事,但无法让它工作:

  • 存储 String , 返回 &str :“生命周期不够长”的生命周期错误
  • 存储 &str , 返回 &str : 相同的生命周期错误
  • Cow<&str>
  • Box<&str>

在使用调试信息以 Release模式构建后,根据 CodeXL 基于时间的采样分析器,这些克隆是我程序目前的瓶颈。现在,我的程序已经足够快了,但我想知道是否有办法避免它们。

最佳答案

您可以通过将字符串包装在 Rc 中并克隆它来避免克隆字符串。克隆一个 Rc 很便宜,因为它包括增加一个计数器:

pub struct Reader {
handle: BufReader<File>,
repeat_state: Option<(NonZeroU32, Rc<String>)>,
}

impl Reader {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Reader, Error> {
let file = File::open(path)?;
let handle = BufReader::new(file);

Ok(Reader {
handle,
repeat_state: None,
})
}

/// get next line, respecting repeat instructions
pub fn next_line(&mut self) -> Option<Rc<String>> {
if self.repeat_state.is_some() {
let (repeats_left, last_line) = mem::replace(&mut self.repeat_state, None).unwrap();

self.repeat_state = NonZeroU32::new(repeats_left.get() - 1)
.map(|repeats_left| (repeats_left, last_line.clone()));

Some(last_line)
} else {
let mut line = Rc::new (String::new());
if self.handle.read_line(Rc::make_mut (&mut line)).is_err() || line.is_empty() {
return None
}

if line.starts_with("repeat ") {
let repeats: Option<u32> = line.chars().skip(7)
.take_while(|c| c.is_numeric())
.collect::<String>().parse().ok();

self.repeat_state = repeats
.and_then(|repeats| NonZeroU32::new(repeats - 1))
.map(|repeats_left| (repeats_left, line.clone()))
}

Some(line)
}
}
}

playground

请注意,Rc 不能在多个线程之间共享。如果你想在线程之间共享字符串,你可以使用 Arc相反。

关于memory-management - 我可以通过持有引用来避免克隆字符串吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52596080/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com