由于添加了明显不相关的指令,我的变量的生命周期是否在变化?

时间:2017-08-01 19:31:47

标签: rust ownership borrowing

我对Rust很陌生,在写一些简单的程序的同时仍在阅读the book以测试我正在学习的内容。

今天我尝试写一个建议作为练习的程序(更准确地说是the end of chapter 8.3的最后一个程序)。由于我还在学习,因此非常慢,我为我添加到cargo build的几乎所有新行都运行了一个新的main.rs。截至目前,它看起来像这样:

use std::io::{self, Write};
use std::collections::{HashMap, HashSet};

enum Command<'a> {
    Add {name: &'a str, unit: &'a str},
    List {unit: &'a str},
    Exit
}

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

    loop {
        let mut cmd = String::new();
        io::stdin().read_line(&mut cmd).unwrap();

        let cmd = match parse_command(&cmd) {
            Ok(command) => command,
            Err(error) => {
                println!("Error: {}!", error);
                continue;
            }
        };

        match cmd {
            Command::Add {name: new_name, unit: new_unit} => {
                let mut u = units.entry("unit1").or_insert(HashSet::new());
                u.insert(new_name);
            },

            Command::List {unit: target_unit} => {},
            Command::Exit => break
        }
    } // end of loop
} // end of main

fn parse_command<'a>(line: &'a String) -> Result<Command<'a>, &'a str> {
    Ok(Command::Exit)
    // ... still need to write something useful ...
}

没有什么复杂的,因为我仍然没有在parse_command函数中写入任何内容,当前只返回Result::Ok(Command::Exit),但是当我尝试编译上面的代码时,我得到以下内容错误:

 
error[E0597]: `cmd` does not live long enough
  --> src/main.rs:34:2
   |
17 |            let cmd = match parse_command(&cmd) {
   |                                           --- borrow occurs here
...
34 |    } // end of loop
   |    ^ `cmd` dropped here while still borrowed
35 | } // end of main
   | -  borrowed value needs to live until here

弄清楚这一点并不奇怪,但我对此错误感到很困惑。是的,我将cmd放在loop的末尾,这没关系,但是为什么借来的价值需要存活到main结束?cmd相关的任何内容都发生在loop内,为什么借来的价值预计会比这更长?

试图弄清楚出了什么问题,我删除了match Command::Add {...}部分内的两条线,所以它看起来像这样:

    match cmd {
        Command::Add {name: new_name, unit: new_unit} => {},
        Command::List {unit: target_unit} => {},
        Command::Exit => break
    }

而且,令我惊讶的是,代码编译时没有错误(即使我需要这些行,所以这只是一个愚蠢的测试)。

我认为这两行没有与我的cmd变量有关,或者是吗?这里发生了什么?我99%肯定我有一些非常愚蠢的东西,但我无法弄明白自己可能是什么。任何帮助都会非常感激!

1 个答案:

答案 0 :(得分:4)

  

是的,我在循环结束时放下cmd,这没关系

不,它不是,这就是编译器告诉你的。 Rust已经完成了它的工作,并且阻止你在程序中插入内存不安全。

您在循环中分配String对其进行引用并从中创建CommandCommand仅表示它包含引用,所有相同的生命周期。然后,代码从Command中取出其中一个引用并尝试将其存储在HashMap中。

循环退出后,HashMap将包含对现已解除分配的String的引用,这将是一件非常糟糕的事情。

  

cmd相关的任何内容都发生在循环

不,它没有。您将对String的引用传递给函数。那时,所有的赌注都没有了。该功能可以执行签名允许的任何,包括:

fn parse_command<'a>(line: &'a String) -> Result<Command<'a>, &'a str> {
    Ok(Command::Add {
        name: line,
        unit: line,
    })
}

您的代码相当于:

use std::collections::HashSet;

fn main() {
    let mut units = HashSet::new();

    {
        let cmd = String::new();
        units.insert(&cmd);
    }

    println!("{:?}", units);
}