代码之家  ›  专栏  ›  技术社区  ›  PitaJ

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

  •  0
  • PitaJ  · 技术社区  · 6 年前

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

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

    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>

    这些克隆是我的程序目前的瓶颈,根据CodeXL基于时间的采样探查器,在构建了带有调试信息的发布模式之后。现在,我的程序虽然很快,但我想知道是否有办法避免它们。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Jmb    6 年前

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

    请注意 Arc 相反。