Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to release a binding before it goes out of scope?

I'm trying to parse a file using regexes:

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

...but the compiler complains because the RegEx's captures() method has a borrow which endures for the lifetime of the match:

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

By the time I get to line.clear();, I'm done with the Match and would like to clear the buffer and move onto the next line in the file without further processing. Is there a good/clean/elegant/idiomatic solution or do I need to just bite the bullet and introduce a subsequent 'if' block?

like image 559
steamer25 Avatar asked Jan 11 '17 21:01

steamer25


1 Answers

Short answer: No.

I'm done with the Match

You may be, but the compiler doesn't know that. Specifically, lifetimes are currently bound to the lexical scope they are defined in. The feature you are looking for is called non-lexical lifetimes. It's not stable now, but it's planned to be enabled in the Rust 2018 edition.

As an example:

fn main() {
    let mut s = String::from("hello");

    let matched = &s[..];
    println!("{}", matched);

    s.clear();

    println!("{}", s);
}

A programmer can tell we are done with matched after we print it, but the compiler says that the borrow lasts until the closing }. The fix is to introduce a scope:

fn main() {
    let mut s = String::from("hello");

    {
        let matched = &s[..];
        println!("{}", matched);
    }
    s.clear();

    println!("{}", s);
}

Your case is more insidious, as the decision to clear the string is interwoven with the value of the borrow of the string itself. Something like this would be my first place to reach:

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

However, your specific case might be able to be transformed to avoid multiple if / if let statements:

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

Which wouldn't look too bad if you introduce a new type and/or method. As a bonus, there's a place for the Regex to live:

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

The original code works as-is when NLL is enabled:

#![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
    }
}
like image 71
Shepmaster Avatar answered Oct 22 '22 12:10

Shepmaster