有没有办法在绑定超出作用域之前释放它?

11

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

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() {}

......但编译器抱怨,因为 RegExcaptures() 方法具有一个借用,该借用持续到匹配的生命周期结束:

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”块?


4
与您的问题无关,为什么要使用.eq()?我看到其他人也这样做。避免使用==的理由是什么?回答:.eq()==都可以用于比较两个值是否相等,但它们在处理某些特殊情况时可能会有所不同。例如,.eq()可以用于比较NaN(非数字)和undefined(未定义)值,而==不能。因此,在某些情况下,为了确保正确的比较结果,使用.eq()可能更可靠。 - Shepmaster
@Shepmaster 我不确定它们是否重复,因为另一个问题处理的是else块(我没有),并且通过早期返回解决了该问题。 - steamer25
1
@Shepmaster 对我来说,也可能对其他人来说,使用Java的经验使我们对使用==进行字符串比较有些犹豫。在Java中,==运算符比较的是字符串的内存偏移/指针,而不是字节/字符的深度比较。 - steamer25
4
我害怕那是Java。很奇怪像Java这样高级的语言,在==方面出现了失误,而Rust这种更低级的语言则偏爱值相等而不是引用相等。 - Shepmaster
5
@par 这可能不是争论的最佳场所,但这更像是“继承使代码难以理解”的说法;我不确定一个equals方法如何免于任何会影响==的继承奇怪现象。昂贵的操作总是会击败那些不能做到程序员想要的快速操作。我们还学到了你不能仅仅把C指定为黄金标准。如果是这样的话,Java就不存在了,更不用说Rust了!说C具有引用相等性很棘手,因为它具有指针值相等性,这是不同的。 - Shepmaster
显示剩余2条评论
1个回答

9

简短回答:不行。

我已经完成了Match

你可能完成了,但编译器并不知道。具体来说,生命周期目前绑定到它们定义的词法作用域中。你要找的功能叫做非词法生命周期。它现在还不稳定,但计划在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
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接