I am trying to return &str as a default value in an if statement
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let value: &str = if args.len() < 3 {
match env::var("DEFAULT") {
Ok(val) => val.as_str(),
Err(_) => "default",
}
} else { args[1].as_str()};
}
This fails with
error[E0597]: `val` does not live long enough
--> tmp.rs:8:24
|
6 | let value: &str = if args.len() < 3 {
| ----- borrow later stored here
7 | match env::var("DEFAULT") {
8 | Ok(val) => val.as_str(),
| --- ^^^ - `val` dropped here while still borrowed
| | |
| | borrowed value does not live long enough
| binding `val` declared here
error: aborting due to previous error; 1 warning emitted
How would I fix this? Moving env::var outside the if statement does not help.
(Title is kinda badly formulated, feel free to update it.)
>Solution :
The request does not make much sense on its face, because env::var always returns a copy of the actual environment variable (and once that’s validated and decoded). val is the owner of that value, there’s nobody else keeping it alive for you.
Hence the message: by not keeping a handle on val, you’re letting it go out of scope and be dropped, thus invalidating any reference you would have taken.
Now there are mutiple ways to handle this:
-
Just use
String, convertdefaultto aString, and pop the relevant value out of theargse.g.
use std::env; fn main() { let mut args: Vec<_> = env::args().collect(); let value = if args.len() < 3 { match env::var("DEFAULT") { Ok(val) => val, Err(_) => "default".to_string(), } } else { args.remove(1) }; }That avoids the reference part, no reference, no issue with not keeping the source alive.
You could even pop the values out of
std::env::Argsby hand: it’s an exact size iterator, so you don’t need to collect it to a vec to know how many values it contains, you can ask it upfront. -
Use
Cow, that lets you unify / abstract overStringand&str:use std::borrow::Cow; use std::env; fn main() { let args: Vec<_> = env::args().collect(); let value: Cow<'_, str> = if args.len() < 3 { match env::var("DEFAULT") { Ok(val) => val.into(), Err(_) => "default".into(), } } else { args[1].as_str().into() }; }That works around the reference, by abstracting over owned / reference entirely.
-
Stow the String in a function-local variable before taking a reference to that:
use std::env; fn main() { let env; let args: Vec<_> = env::args().collect(); let value = if args.len() < 3 { match env::var("DEFAULT") { Ok(val) => { env = val; &env }, Err(_) => "default", } } else { &args[1] }; }That solves the reference by keeping its source alive.