简体   繁体   中英

How do I return local data with a recursive function in Rust?

I'm struggling to write a recursive algorithm in Rust. With the following code:

use std::collections::HashMap;

enum Error {
    Bad,
    ReallyBad,
}

fn expand_symbols<'a, T: AsRef<str>>(
    symbols: &'a [T],
    ops: &HashMap<String, String>,
    user_ops: &'a HashMap<String, String>,
) -> std::result::Result<Vec<&'a str>, Error> {
    if symbols.iter().all(|x| ops.get(x.as_ref()).is_some()) {
        let symbols = symbols.iter().map(|x| x.as_ref()).collect();
        return Ok(symbols);
    }

    let mut expanded: Vec<&str> = vec![];
    for s in symbols {
        let s = s.as_ref();
        if ops.contains_key(s) || s.parse::<i32>().is_ok() {
            expanded.push(s);
        } else {
            let mut resolved = user_ops
                .get(s)
                .ok_or(Error::Bad)?
                .split_ascii_whitespace()
                .collect::<Vec<_>>();
            expanded.append(&mut resolved);
        }
    }
    expand_symbols(&expanded, ops, user_ops)
}

I get:

error[E0515]: cannot return value referencing local variable `expanded`
  --> src/main.rs:32:5
   |
32 |     expand_symbols(&expanded, ops, user_ops)
   |     ^^^^^^^^^^^^^^^---------^^^^^^^^^^^^^^^^
   |     |              |
   |     |              `expanded` is borrowed here
   |     returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.

However, if I change the last statement to:

Ok(expanded)

it works, but it's not longer recursive.

I understand the idea that I'm trying to return a value borrowed from a local frame, but I think this is safe based on the second example. How can I tell the compiler that?

Note: I'm using AsRef because I want to be able to pass both a Vec<String> and Vec<&str> to expand_symbols() . Maybe I need to forget about that?

With Ok(expanded) the variable expanded is moved out of the function, meaning no reference to it existing after the function returned. So, the second sample, if you meant by Ok(expanded) , is not same as the original one.

To address the issue I think you can pass a mutable reference to symbols as the first parameter of the function, and do in-place edit on it instead of creating a new local vector, explanded .

fn expand_symbols<'a>(
    symbols: &'a mut Vec<&'a str>,
    ops: &HashMap<String, String>,
    user_ops: &'a HashMap<String, String>,
) -> std::result::Result<&'a Vec<&'a str>, Error> {
    if symbols.is_empty() || symbols.iter().all(|x| ops.get(*x).is_some()) {
         return Ok(symbols);
    }

    let mut unresolved: Vec<&str> = vec![]; 
    let mut i = 0;
    while i < symbols.len() {
        let s = symbols[i];
        if ops.contains_key(s) || s.parse::<i32>().is_ok() {
            i += 1;
        } else {
            unresolved.push(symbols.remove(i));
        }
    }

    for s in unresolved.iter() {
        let mut resolved = user_ops
            .get(*s)
            .ok_or(Error::Bad)?
            .split_ascii_whitespace()
            .collect::<Vec<_>>();
        symbols.append(&mut resolved);
    };

    expand_symbols(symbols, ops, user_ops)
}

playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM