【问题标题】:re.captures error: borrowed value does not live long enoughre.captures 错误:借来的值不够长
【发布时间】:2018-09-09 00:53:20
【问题描述】:

尝试在 https://doc.rust-lang.org/book/2018-edition/ch08-03-hash-maps.html 完成 Rust 书的“哈希映射”一章,使用以下代码:

extern crate regex;

use std::collections::HashMap;
use std::io;
use regex::Regex;

fn get_command() -> String {
    let mut input_cmd = String::new();

    io::stdin().read_line(&mut input_cmd)
        .expect("Failed to read command");
    let input_cmd = input_cmd.trim();

    input_cmd.to_string()
}

fn main() {
    println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");

    let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();

    let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
    let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
    let list_all_re = Regex::new("^List everyone$").unwrap();

    loop {
        let input_cmd = get_command();
        let caps = add_to_dept_re.captures(&input_cmd).unwrap();

        if add_to_dept_re.is_match(&input_cmd) {
            let dept_name = caps.get(2).unwrap().as_str();
            let employee_name = caps.get(1).unwrap().as_str();

            println!("Adding person");
            employees_by_dept.entry(&dept_name)
                .or_insert_with(Vec::new)
                .push(employee_name);
        } else if list_in_dept_re.is_match(&input_cmd) {
            println!("Listing people");
        } else if list_all_re.is_match(&input_cmd) {
            println!("Listing everyone");
        } else if input_cmd == "Quit" {
            break;
        } else {
            println!("Invalid command");
            break;
        }
    }

    println!("Bye!");
}

但我明白了:

error[E0597]: `input_cmd` does not live long enough
  --> src/main.rs:28:45
   |
28 |         let caps = add_to_dept_re.captures(&input_cmd).unwrap();
   |                                             ^^^^^^^^^ borrowed value does not live long enough
...
48 |     }
   |     - `input_cmd` dropped here while still borrowed
...
51 | }
   | - borrowed value needs to live until here

尝试了.captures(&amp;input_cmd.clone()) 和其他各种方法,但无济于事。有什么想法吗?

【问题讨论】:

  • 您认为为什么需要引用作为地图的键? HashMap&lt;&amp;str, TVec&lt;&amp;str&gt; 都不是非常有用/方便的类型。您很可能希望拥有 String

标签: rust


【解决方案1】:

Rust 内存安全规则阻止了这种方法:您的 HashMap 值比插入的项目长。

请参阅下面的嵌入式 cmets,尤其是本书的 Ownership chapter

fn main() {
    let mut employees_by_dept: HashMap<&str, Vec<&str>> = HashMap::new();

    let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
    let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
    let list_all_re = Regex::new("^List everyone$").unwrap();

    loop {
        let input_cmd = get_command();
        let caps = add_to_dept_re.captures(&input_cmd).unwrap();// <--- input_cmd 
                                                                //is borrowed here

        // ... code for getting dept_name and employee_name references
        //     and inserting into HashMap omitted

    } // <----- The String input_cmd is dropped here (memory is freed)
      // this implies that dept_name and employee_name references 
      // points to deallocated memory

    // ... At this point you will have a live employees_by_dept HashMap
    //     that contains references to deallocated memory

    println!("Bye!");
}

改为让 HashMap 获得键/项目值的所有权:

fn main() {
    println!("Add someone by typing e.g. \"Add Sally to Engineering\", list everyone in a department by typing e.g. \"List everyone in     Sales\", or list everyone by typing \"List everyone\". To quit, type \"Quit\".");

    let mut employees_by_dept: HashMap<String, Vec<String>> = HashMap::new();

    let add_to_dept_re = Regex::new("^Add ([A-Za-z]+) to ([A-Za-z]+)$").unwrap();
    let list_in_dept_re = Regex::new("^List everyone in ([A-Za-z]+)$").unwrap();
    let list_all_re = Regex::new("^List everyone$").unwrap();

    loop {
        let input_cmd = get_command();
        let caps = add_to_dept_re.captures(&input_cmd).unwrap();

        if add_to_dept_re.is_match(&input_cmd) {
            let dept_name = caps.get(2).unwrap().as_str();
            let employee_name = caps.get(1).unwrap().as_str();

            println!("Adding person");

            employees_by_dept
                .entry(dept_name.to_string())
                .or_insert_with(Vec::new)
                .push(employee_name.to_string());
        } else if list_in_dept_re.is_match(&input_cmd) {
            println!("Listing people");
        } else if list_all_re.is_match(&input_cmd) {
            println!("Listing everyone");
        } else if input_cmd == "Quit" {
            break;
        } else {
            println!("Invalid command");
            break;
        }
    }
    println!("Bye!");
}

【讨论】:

  • 就这么简单...只需使用String 而不是&amp;str...是的,可以很好地适应这种变化!我显然还没有弄清楚 Rust 的所有权系统,也没有弄清楚 String&amp;str 之间的区别。无论如何,我才刚刚开始,希望它很快就会为我所接受。
猜你喜欢
  • 2016-09-20
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 2014-12-26
  • 2019-11-29
  • 2017-03-11
相关资源
最近更新 更多