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

时间:2018-10-01 17:31:23

标签: memory-management rust

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

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

SELECT * FROM child
ORDER BY
  CASE parentId
  WHEN someParentId THEN 1
  ELSE 2 END,
  parentId; 

Playground

问题是我必须每次克隆保留的重复值才能同时保留并返回它。我想通过返回(并存储)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()) ); } 来避免这些昂贵的克隆。我已经尝试了几种方法,但是无法使其正常工作:

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

根据CodeXL基于时间的采样探查器,这些克隆现在是我程序的瓶颈,它是在带有调试信息的发布模式下构建的。现在,我的程序已经足够快了,但是我想知道是否有避免它们的方法。

1 个答案:

答案 0 :(得分:2)

您可以通过将字符串包装在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

相关问题