尝试在循环内部设置循环外的变量时,值的活动时间不够长

时间:2017-08-13 20:06:39

标签: rust

我从this example开始使用discord-rs制作Discord聊天机器人。一切正常,编译正常,直到我试图修改在循环之前声明的值。

我试图将prefix更改为在命令中输入的第二个单词:

extern crate discord;
use discord::Discord;
use discord::model::Event;
use std::env;

fn main() {
    let discord = Discord::from_bot_token(&env::var("DISCORD_TOKEN").expect("Expected token"))
        .expect("login failed");
    let (mut connection, _) = discord.connect().expect("connect failed");
    println!("Ready.");

    let mut prefix = "!";

    loop {
        match connection.recv_event() {
            Ok(Event::MessageCreate(message)) => {
                if message.content.starts_with(prefix) {
                    let msg: Vec<&str> = message.content[prefix.len()..].split(" ").collect();
                    // message.content gets split into separate words and is checked for the command
                    match msg[0] {
                        "ag3nprefix" => {
                            prefix = msg[1];
                            // ^ here is the line that makes it not compile
                            let text = format!("AG3N's command prefix changed to: {}", prefix);
                            let _ = discord.send_message(message.channel_id, &text, "", false);
                        }
                        &_ => {}
                    }
                }
            }
            Ok(_) => {}
            Err(discord::Error::Closed(code, body)) => {
                println!("Gateway closed on us with code {:?}: {}", code, body);
                break;
            }
            Err(err) => println!("Receive error: {:?}", err),
        }
    }
}

我尝试过各种各样的方式,但没有任何方法可行。这是编译器的错误:

error[E0597]: `message.content` does not live long enough
  --> src/main.rs:38:9
   |
19 |                     let msg: Vec<&str> = message.content[prefix.len()..].split(" ").collect();
   |                                          --------------- borrow occurs here
...
38 |         }
   |         ^ `message.content` dropped here while still borrowed
39 |     }
40 | }
   | - borrowed value needs to live until here

1 个答案:

答案 0 :(得分:2)

一个较小的例子

这是问题的MCVE

fn main() {
    let mut prefix = "!";
    let mut i = 0;

    loop {
        let event = String::from("hello");

        match i {
            0 => prefix = &event,
            _ => println!("{}", prefix),
        }

        i += 1;
    }
}

Rust 2015

error[E0597]: `event` does not live long enough
  --> src/main.rs:9:28
   |
9  |             0 => prefix = &event,
   |                            ^^^^^ borrowed value does not live long enough
...
14 |     }
   |     - `event` dropped here while still borrowed
15 | }
   | - borrowed value needs to live until here

Rust 2018

error[E0597]: `event` does not live long enough
  --> src/main.rs:9:27
   |
9  |             0 => prefix = &event,
   |                           ^^^^^^ borrowed value does not live long enough
10 |             _ => println!("{}", prefix),
   |                                 ------ borrow used here, in later iteration of loop
...
14 |     }
   |     - `event` dropped here while still borrowed

核心问题是在每次迭代结束时删除event。但是,代码尝试在后续迭代中使用prefix(对event的引用)。如果允许这样做,您将访问无效内存,从而导致未定义的行为。 Rust不允许这种情况发生。

您需要更改代码,以便event(或您需要的部分)比任何一个循环迭代都更长。

应用于原始代码

借用问题的黄金法则是确定谁拥有变量。编译器错误消息可以帮助您:

  

`message.content`活得不够长

好的,我们需要查看message.contentmessage。谁拥有那个?我们已将枚举与所有权转移到名为message的本地变量,因此变量message 是所有者:

Ok(Event::MessageCreate(message)) => {

编译器错误消息同意,因为此错误指向message在范围内的块(由于技术原因,实际上是match括号):

^ `message.content` dropped here while still borrowed

您正在尝试对该字符串进行引用,并将引用存储在需要比单循环迭代更长寿的位置。编译器阻止您将内存不安全引入程序。在其他语言中,您可以编写此代码,并且在将来的某个时候,您的程序将崩溃(最多)或泄漏敏感信息或允许注入代码(最坏情况下)。

相反,在循环中分配String。因为它有自己的分配,它的寿命可能比message长。您还需要更改prefix原始值的类型,并将调用更改为starts_with

let mut prefix = "!".to_string();

loop {
    match connection.recv_event() {
        Ok(Event::MessageCreate(message)) => {
            if message.content.starts_with(&prefix) {
                let msg: Vec<_> = message.content[prefix.len()..].split(" ").collect();
                // message.content gets split into separate words and is checked for the command
                match msg[0] {
                    "ag3nprefix" => {
                        prefix = msg[1].to_string();
                        // ^ here is the line that makes it not compile
                        let text = format!("AG3N's command prefix changed to: {}", prefix);
                        let _ = discord.send_message(message.channel_id, &text, "", false);
                    }
                    &_ => {}
                }
            }
        }
        Ok(_) => {}
        Err(discord::Error::Closed(code, body)) => {
            println!("Gateway closed on us with code {:?}: {}", code, body);
            break;
        }
        Err(err) => println!("Receive error: {:?}", err),
    }
}
let _ = discord.send_message(message.channel_id, &text, "", false);

不要忽视错误。如果您不想处理它,只需添加.expect("I didn't handle this error")