I am trying to return a &str
from an environment variable with a default value if it doesn't exist:
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
How would I fix this? Moving env::var
outside the if statement does not help.
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
, convert default
to a String
, and pop the relevant value out of the args
e.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::Args
by 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 over String
and &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.