How to avoid filter_map() gives error "returns a value referencing data owned by the current function" in Rust?

Viewed 655

I'm trying to do some exercise using filter_map. The function below creates a BufRead then picks lines that match a regular expression. I expect the output to be "aaa", but the compiler gives me the error:

returns a value referencing data owned by the current function" at line 7 `reg.captures(ss)`.

I know that I can't return a value referencing data owned by a function. But I don't know how to avoid it here. When the type of line is a &str, it works. But here, the type of line is a std::string::String.

Would you give me some hint or direct me to some documents. I have read some documents but haven't understood how to avoid the error.

use regex::Regex; // 1.4.1
use std::io::{self, BufRead as _};

fn main() {
    let reg = Regex::new(r"^(a+)").unwrap();
    io::Cursor::new(b"aaa\nbbb\nccc")
        .lines()
        .filter_map(|line| {
            let ss = &line.unwrap()[..];
            reg.captures(ss)
        })
        .map(|cap| cap[1].to_string())
        .for_each(|x| println!("{}", x));
}
2 Answers

Inside your mapping closure, the type of line is Result<String, _>, which means that it owns the String. However, reg.captures() returns a value that contains references to slices within that string. A function cannot return references to values that it owns because those values will be dropped when the function returns, making the reference invalid.

Since you are creating a new owned value immediately after the filter_map anyway, you can just move that part inside, so that the reference is only used within the closure:

io::Cursor::new(b"aaa\nbbb\nccc")
    .lines()
    .filter_map(|line| {
        let ss = &line.unwrap()[..];
        reg.captures(ss).map(|cap| cap[1].to_string())
    })
    .for_each(|x| println!("{}", x));
use regex::Regex; // 1.4.1
use std::io::{self, BufRead as _};

fn main() {
    let reg = Regex::new(r"^(a+)").unwrap();
    io::Cursor::new(b"aaa\nbbb\nccc")
        .lines()
        .filter_map(|line| {
            let ss = &line.unwrap()[..];
            reg.captures(ss)
               // Cannot returns referencing `&str` owned by the current function(closure),
               // but a owned captured substring here
               .map(|cap| cap[1].to_string())
        })
        .for_each(|x| println!("{}", x));
}
Related