【问题标题】:Is there a way to release a binding before it goes out of scope?有没有办法在绑定超出范围之前释放绑定?
【发布时间】:2017-05-26 20:28:33
【问题描述】:

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

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

【问题讨论】:

  • 与您的问题无关;你为什么使用.eq(?我见过其他人也这样做。避免== 背后的原因是什么?
  • @Shepmaster 就我而言,可能对其他人也是如此,Java 的经验让我们对使用 == 进行字符串比较感到有些胆怯。在 Java 中,== 运算符比较字符串的内存偏移量/指针,而不是字节/字符的深度比较。
  • 我担心它是 Java。奇怪的是,像 Java 这样的高级语言在 == 上犯了这样的错误,而 Rust,一种低得多的语言,更喜欢值相等而不是引用相等。
  • @par 这可能不是争论它的最佳场所,但这听起来更像是“继承使代码难以理解”;我不确定equals 方法如何避免任何会影响== 的继承异常。做正确的事情的昂贵操作总是会击败不做程序员想要的快速操作。我们还了解到,您不能只将 C 视为黄金标准。如果是这样的话,Java 将不存在,更不用说 Rust!很难说 C 具有引用相等性,因为它具有 指针的值相等性,这是不同的。

标签: rust borrow-checker


【解决方案1】:

简短回答:不。

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
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    相关资源
    最近更新 更多