Variable does not live long enough error in Rust - rust

Rust beginner here and just doing some learning projects, I encountered the path does not live long enough Error. I tried to find answers, but none of them helped me understand my core problem. I tried multiple ways to fix but nothing helped.
Line of code match Path::new(&path).extension().and_then(OsStr::to_str){ throws the mentioned error . And the error specifically goes away when i remove this langs.insert(ext, 1); line of code. I cant understand why that line causes all the problem??
main.rs (playground)
fn iterate_files(path: String, langs: &mut HashMap<&str, u16>){
let files = fs::read_dir(&path);
match &files{
Ok(_) => {
for file in files.unwrap(){
let path: PathBuf = file.unwrap().path();
let string_path: String = path.display().to_string();
let temp = Path::new(&string_path).file_name().unwrap();
if is_safe_to_iterate(temp){
continue;
}
match Path::new(&path).extension().and_then(OsStr::to_str){
None => {
iterate_files(string_path, langs);
continue;
},
Some(text) => {
let ext: &str = text;
if langs.contains_key(ext){
langs.insert(ext, 1);
}
}
}
println!("{}", path.display());
}
},
Err(_) => {
println!("Illegal File Encountered booom!! {}", path);
},
}
}
Full error message:
error[E0597]: `path` does not live long enough
--> src/lib.rs:24:33
|
12 | fn iterate_files(path: String, langs: &mut HashMap<&str, u16>) {
| - let's call the lifetime of this reference `'1`
...
24 | match Path::new(&path).extension().and_then(OsStr::to_str) {
| ^^^^^ borrowed value does not live long enough
...
32 | langs.insert(ext, 1);
| -------------------- argument requires that `path` is borrowed for `'1`
...
38 | }
| - `path` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error

Related

Rust: Move issue in while loop

I have the following rust code and for the life of me I cant workout why I am getting a borrow-checker issue with this value and why does it get moved as part of a while loop.
Code:
#[tokio::main]
async fn main() -> Fallible<()> {
let config = Config::new().expect("config can't be loaded");
logger::config(&config.log);
let bn = Binance::with_credential(
&config.exchanges.binance_com.api_key,
&config.exchanges.binance_com.api_secret,
);
let mut db = Database::new(config).await;
match bn.user_stream_start()?.await {
Ok(_) => {
let mut ws = BinanceWebsocket::default();
for sub in vec![Subscription::Trade("btcusdt".to_string())] {
ws.subscribe(sub).await?;
}
while let Some(msg) = ws.try_next().await? {
db.write(msg).await?
}
}
Err(e) => error!("Error obtaining stream: {}", e),
};
Ok(())
}
Error:
error[E0382]: use of moved value: `db`
--> x/src/main.rs:35:17
|
24 | let mut db = Database::new(config).await;
| ------ move occurs because `db` has type `Database`, which does not implement the `Copy` trait
...
35 | db.write(msg).await?
| ^^ value moved here, in previous iteration of loop
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0382`.
Any help much appreciated

How to return reference to value inside option while conditionaly modifying outer option

I have token inside Option. Before each operation, I need to check if token is valid or not. it might be expired. if it is expired, I want to set outer option to None and return Error so next time I will just get "No Token" Error.
#[derive(Debug)]
struct Token {
exp : u64
//other cool stuff
}
fn is_valid<'a>(v : &'a mut Option<Token> ) -> Result<&'a mut Token, String> {
match v {
None => Err("No Token".into()),
Some(v_) => {
if v_.exp > 10 {
*v = None;
Err("Token Expired".into())
}
else {
Ok(v_)
// Err("erlgnerlg".into()) //commenting this out and comenting upper line also work.
}
}
}
}
fn main() {
let mut v = Some(Token { exp : 69 });
//expecting "Token Expired" error
println!("{:?} ", is_valid(&mut v));
//expecting "No Token" error
println!("{:?} ", is_valid(&mut v));
}
but it fails to compile.
error[E0506]: cannot assign to `*v` because it is borrowed
--> src/main.rs:13:17
|
8 | fn is_valid<'a>(v : &'a mut Option<Token> ) -> Result<&'a mut Token, String> {
| -- lifetime `'a` defined here
...
11 | Some(v_) => {
| -- borrow of `*v` occurs here
12 | if v_.exp > 10 {
13 | *v = None;
| ^^^^^^^^^ assignment to borrowed `*v` occurs here
...
17 | Ok(v_)
| ------ returning this value requires that `v.0` is borrowed for `'a
if I dont assing None to outer option, my code compile. also in else block, if I return Err instead of Ok, it compiles fine. why does this happen and how can I solve this problem ?
Firstly, we can get another compiler error if we replace *v = None with let _ = v.take() (which does exactly the same thing). This yields the classic "second mutable borrow occurs here":
10 | Some(v_) => {
| -- first mutable borrow occurs here
11 | if v_.exp > 10 {
12 | let _ = v.take();
| ^ second mutable borrow occurs here
...
16 | Ok(v_)
| ------ returning this value requires that `v.0` is borrowed for `'a`
And it makes sense: First, we destructured the borrowed Option to take a look inside, but then we want to change it while still having the reference to the inside of the Option lying around.
We can however capture all of your conditional logic with one match statement, using match guards:
match v {
None => Err("No Token".into()),
Some(v_) if v_.exp > 10 => {
dbg!(v_.exp); // use v_ in _some_ way
let _ = v.take(); // same as *v = None
Err("Token Expired".into())
}
Some(v_) => Ok(v_)
}
I am not entirely sure, why the compiler gets stuck on your version, but here is what I think happened: Your match arm returns a reference to the inner Token value in some way, thus the compiler deduces that this reference must be kept alive for the entire scope of the arm.
With the match guards, it can deduce that we never touch the contained value again and shorten the lifetime of the inner borrow (the one we implicitly did when matching against Some(v_) if ...). Note that the lifetime of this borrow ends precisely when we drop the previously contained value, as we can still use it in the line dbg!(v_.exp).
Once this inner borrow is over, the outer borrow v is the only reference left and it is fine to touch the outer value in the match arm.
The match mutably borrows v (for v_), so you're not allowed to use it anymore. You need to use v_ instead - at least until it's dropped.
I also changed the names to v_opt and v for readability:
match v_opt {
None => Err("No Token".into()),
Some(v) if v.exp > 10 => {
drop(v);
*v_opt = None;
Err("Token Expired".into())
}
Some(v_) => Ok(v_)
}

nom & borrowed value does not live long enough error

Trying to work with nom and iterate over my result, but I can't figure out why this borrowed value doesn't live long enough. Still new to Rust and have been head-banging for hours. Would appreciate help!
use anyhow;
use nom::{
bytes::complete::{tag, take},
character::complete::newline,
combinator::rest,
multi::separated_list1,
sequence::separated_pair,
IResult,
};
pub fn parse(raw: String) -> anyhow::Result<()> {
let (_, lines) = parse_multiline(&raw)?;
for line in lines.iter() {
let (label, value) = line;
println!("label: {}, value: {}", label, value);
}
Ok(())
}
fn parse_multiline(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
separated_list1(
newline,
separated_pair(
take(14usize),
tag(" : "),
rest,
),
)(i)
}
And the error:
error[E0597]: `raw` does not live long enough
--> src/parser/person.rs:12:38
|
12 | let (_, lines) = parse_multiline(&raw)?;
| ----------------^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `raw` is borrowed for `'static`
...
21 | }
| - `raw` dropped here while still borrowed
In parse_multiline() you return IResult<&str, Vec<(&str, &str)>>. This means that if your parse_multiline() fails, then it returns nom::Err<nom::error::Error<&str>> which has a reference to i/&raw.
Thus in parse(), parse_multiline(raw)? propagates that error further, which would return a reference to raw, which would not live long enough.
If you want to retain fn parse(raw: String), then the you can use Result::map_err() and then nom::Err::map(), to convert the &str into an owned String on error.
// nom = "6.1"
use nom::error::Error;
pub fn parse(raw: String) -> anyhow::Result<()> {
let (_, lines) = parse_multiline(&raw)
.map_err(|err| err.map(|err| Error::new(err.input.to_string(), err.code)))?;
// ...
}

Mutably borrowing in match statement and result

I'm trying to determine if a container has an object and return the found object if it does, or add it if it doesn't.
I've found Rust borrow mutable self inside match expression
which has an answer which says what I am trying to do can't (couldn't?) be done.
In my situation, I've got some objects that have vectors of children. I don't want to expose the internals of my object, because I may want to change the representation underneath.
How can you resolve the need to mutably borrow in different match arms in Rust? seems to suggest I may be able to do what I want if I get the lifetimes correct, but I haven't been able to figure out how.
Here's a representation of the issue I'm having:
fn find_val<'a>(container: &'a mut Vec<i32>, to_find: i32) -> Option<&'a mut i32> {
for item in container.iter_mut() {
if *item == to_find {
return Some(item);
}
}
None
}
fn main() {
let mut container = Vec::<i32>::new();
container.push(1);
container.push(2);
container.push(3);
let to_find = 4;
match find_val(&mut container, to_find) {
Some(x) => {
println!("Found {}", x);
}
_ => {
container.push(to_find);
println!("Added {}", to_find);
}
}
}
playground
The error I get is:
error[E0499]: cannot borrow `container` as mutable more than once at a time
--> src/main.rs:24:13
|
19 | match find_val(&mut container, to_find) {
| --------- first mutable borrow occurs here
...
24 | container.push(to_find);
| ^^^^^^^^^ second mutable borrow occurs here
...
27 | }
| - first borrow ends here
Put the change in a function, and use early return instead of an else branch:
fn find_val_or_insert(container: &mut Vec<i32>, to_find: i32) {
if let Some(x) = find_val(&container, to_find) {
println!("Found {}", x);
return; // <- return here instead of an else branch
}
container.push(to_find);
println!("Added {}", to_find);
}
See also Mutable borrow more than once and How to update-or-insert on a Vec?

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

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
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").

Resources