How to clean this up

Hi,
It's me again. I would like to clean up the nested, nested, nested if let statements as shown in the this_works() function in the playground code below, and replace it with the iterator chain as shown in the this_fails_to_compile() function, but that, err, fails to compile.

I understand the error, but I don't know how to fix it.

Any thoughts?

--wpd

use regex::Regex;
use std::collections::BTreeSet;
use std::io::{BufRead, Cursor};

fn this_fails_to_compile() -> Vec<String>
{
    let essid_re = Regex::new(r#"ESSID:"(.+)""#).expect("Fix the broken RE you wrote!");
    let file = Cursor::new(b"first\n    ESSID:\"ABC\"\nthird\n");
    
    // Using the following gets a "returns a value referencing data owned by the current function" error
    // on the `essid_re.captures(&line)` line
    
    let networks: BTreeSet<_> = file //                 * Use a BTreeSet to eliminate duplicates and to sort
        .lines() //                                     * split the input into lines
        .filter_map(Result::ok) //                      * only grab lines that convert to UTF-8 without error
        .filter_map(|line| essid_re.captures(&line)) // * match lines containing ESSID:"(.+)" (discard lines not matching that)
        .filter_map(|m| m.get(1)) //                    * grab the .+ part of ESSID"(.+)"
        .map(|m| m.as_str().to_string()) //             * convert it to a String
        .collect(); //                                  * and collect all of that into a Vec<String>
    networks.into_iter().collect::<Vec<_>>() //         * convert to Vec<String>
}

fn this_works() -> Vec<String> {
    let essid_re = Regex::new(r#"ESSID:"(.+)""#).expect("Fix the broken RE you wrote!");
    let file = Cursor::new(b"first\n    ESSID:\"ABC\"\nthird\n");

    let mut networks = BTreeSet::<_>::new();
    for line in file.lines() {
        if let Some(line) = line.ok() {
            if let Some(m) = essid_re.captures(&line) {
                if let Some(ssid) = m.get(1) {
                    networks.insert(ssid.as_str().to_string());
                }
            }
        }
    }

    networks.into_iter().collect::<Vec<_>>()
}

fn main() {
    println!("This works: {:#?}", this_works());
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing function parameter `line`
  --> src/main.rs:16:28
   |
16 |         .filter_map(|line| essid_re.captures(&line)) // * match lines containing ESSID:"(.+)" (discard lines not matching that)
   |                            ^^^^^^^^^^^^^^^^^^-----^
   |                            |                 |
   |                            |                 `line` is borrowed here
   |                            returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground` due to previous error

Here:

.filter_map(|line| essid_re.captures(&line)) // * match lines containing ESSID

the closure gets a String and tries to return a &str Captures<'_> from it. To fix this, finish your conversion from String to String within the closure:

        .filter_map(|line| {
            essid_re.captures(&line)    //              * match lines containing ESSID:"(.+)" (discard lines not matching that)
                .and_then(|m| m.get(1)) //              * grab the .+ part of ESSID"(.+)"                
                .map(|m| m.as_str().to_string()) //     * convert it to a String
        })

Playground.

1 Like

Thank you again @quinedot. This gives me a much better appreciation and understanding of how to use the and_then() and or_else() Boolean operators on Result.
and_then() he had another tool to add to his trusty Rusty tool belt :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.