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

有没有办法在绑定超出范围之前释放它?

  •  11
  • steamer25  · 技术社区  · 8 年前

    我正在尝试使用正则表达式解析文件:

    extern crate regex; // 1.0.1
    
    use regex::Regex;
    
    fn example(
        section_header_pattern: Regex,
        section_name: &str,
        mut line: String,
        mut is_in_right_section: bool,
    ) {
        loop {
            if let Some(m) = section_header_pattern
                .captures(&line)
                .and_then(|c| c.get(1))
            {
                is_in_right_section = m.as_str().eq(section_name);
                line.clear();
                continue;
            }
        }
    }
    
    fn main() {}
    

    …但编译器会抱怨,因为 RegEx 是的 captures() 方法具有在匹配的整个生命周期内持续的借用:

    error[E0502]: cannot borrow `line` as mutable because it is also borrowed as immutable
      --> src/main.rs:17:13
       |
    13 |             .captures(&line)
       |                        ---- immutable borrow occurs here
    ...
    17 |             line.clear();
       |             ^^^^ mutable borrow occurs here
    18 |             continue;
    19 |         }
       |         - immutable borrow ends here
    

    等我到了 line.clear(); ,我受够了 Match 并且希望清除缓冲区并移动到文件中的下一行,而无需进一步处理。是否有一个好的/干净的/优雅的/惯用的解决方案,或者我需要咬紧牙关,引入后续的“if”块?

    1 回复  |  直到 6 年前
        1
  •  9
  •   Shepmaster Tim Diekmann    6 年前

    简单的回答是:没有。

    我受够了 Match

    你可能知道,但编译器不知道。具体而言,生命期当前绑定到它们在中定义的词法范围。您正在查找的功能称为 non-lexical lifetimes .它现在不稳定,但计划在Rust 2018版中启用。

    例如:

    fn main() {
        let mut s = String::from("hello");
    
        let matched = &s[..];
        println!("{}", matched);
    
        s.clear();
    
        println!("{}", s);
    }
    

    程序员可以告诉我们已经完成了 matched 在我们打印之后,但编译器说借阅将持续到结束 } 解决方案是引入一个范围:

    fn main() {
        let mut s = String::from("hello");
    
        {
            let matched = &s[..];
            println!("{}", matched);
        }
        s.clear();
    
        println!("{}", s);
    }
    

    您的情况更加阴险,因为清除字符串的决定与字符串本身的借用值交织在一起。像这样的东西将是我第一个到达的地方:

    fn main() {
        let mut s = String::from("hello");
    
        let do_clear;
    
        {
            let matched = &s[..];
            println!("{}", matched);
            do_clear = matched.contains("ll");
        }
    
        if do_clear {
            s.clear();
        }
    
        println!("{}", s);
    }
    

    然而,您的特定案例可能能够被转换以避免多个 if / if let 声明:

    let is_in_right_section = section_header_pattern.captures(&line)
        .and_then(|c| c.get(1))
        .map_or(false, |m| m.as_str() == section_name);
    
    if is_in_right_section {
        line.clear();
        continue;
    }
    

    如果引入新的类型和/或方法,这看起来不会太糟糕。作为奖励,有一个地方 Regex 生活:

    struct Section(Regex);
    
    impl Section {
        fn is(&self, s: &str, section: &str) -> bool {
            self.0
                .captures(s)
                .and_then(|c| c.get(1))
                .map_or(false, |m| m.as_str() == section)
        }
    }
    
    // ----
    
    if section.is(&line, section_name) {
        line.clear();
        continue;
    }
    

    启用NLL时,原始代码按原样工作:

    #![feature(nll)]
    
    extern crate regex; // 1.0.1
    
    use regex::Regex;
    
    fn main() {
        let section_header_pattern = Regex::new(".").unwrap();
        let section_name = "";
        let mut line = String::new();
        let mut is_in_right_section = false;
    
        loop {
            if let Some(m) = section_header_pattern
                .captures(&line)
                .and_then(|c| c.get(1))
            {
                is_in_right_section = m.as_str().eq(section_name);
                line.clear();
                continue;
            }
    
            return; // I don't really want to loop
        }
    }