Value does not live long enough when trying to set a variable outside a loop from inside the loop

白昼怎懂夜的黑 提交于 2021-02-17 04:29:37

问题


I'm making a Discord chat bot using discord-rs, starting from this example. Everything was working and compiling fine until I tried to modify a value that is declared before a loop.

I'm trying to change prefix to the second word that is entered in a command:

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),
        }
    }
}

I tried doing it various ways, but nothing would work. Here's the error from the compiler:

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:


A smaller example

Here's a MCVE of the problem:

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

The core issue is that event is dropped at the end of each iteration. However, the code attempts to use prefix, a reference to event, in a subsequent iteration. If that were allowed to happen, you'd be accessing invalid memory, causing undefined behavior. Rust disallows this from happening.

You need to change the code so that event (or the part of it you need) lives longer than any one loop iteration.

Applied to the original code

The golden rule for borrowing problems is to identify who owns a variable. The compiler error messages help you here:

`message.content` does not live long enough

Ok, so we need to look at message.content or just message. Who owns that? We've matched a enum and transferred ownership to a local variable called message, so the variable message is the owner:

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

The compiler error message agrees, as this error points to the block where message is in scope (actually the match braces for technical reasons):

^ `message.content` dropped here while still borrowed

You are trying to take a reference to that string and store the reference somewhere that needs to live longer than a single loop iteration. The compiler has stopped you from introducing memory unsafety into your program. In other languages, you'd write this code and at some point in the future your program would crash (at best) or leak sensitive information or allow injecting code in (at worst).

Instead, allocate a String inside the loop. Because it has its own allocation, it can live longer than message. You also need to change the type of the original value of prefix and change the call to 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);

DO NOT IGNORE ERRORS. If you don't want to handle it, just add .expect("I didn't handle this error").



来源:https://stackoverflow.com/questions/45664456/value-does-not-live-long-enough-when-trying-to-set-a-variable-outside-a-loop-fro

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!