What is the more concise way to get env vars?

I used a lot of methods, wrapping them with structopts env macro attribute as one of them, and although dotenv crate seems to be the way to go, it's a pain to use and something like std::env::var("VAR_I_NEED").unwrap() just looks plain ugly.

What are your preferred ways to deal with it?

What's ugly about it? It's two function calls. (It would be only one if you actually handled the missing case, as the type system gently nudges you, instead of plain crashing.)

it gets ugly pretty quickly if you have 10 of those and for all of them for example you don't use just unwrap but instead provide some default value.

I understand that it's a rare case one would have such cumbersome handling, I just wanted to check if someone came up with something prettier :slight_smile:

You mean like, unwrap_or("0")? Because that's about as short and non-ugly as it reasonably gets.

Anyway, if you find yourself repeating the same code 10 times, then you are missing some sort of abstraction and should refactor your code – this is not the fault of environment variables or the language.

I could imagine a simple function to contract the lookup and the default, but it hardly gets any less repetitive. You could perhaps make it typed and generic if you need parsing, too:

fn env_or<T>(name: &str, default: T) -> T
where
    T: FromStr,
{
    std::env::var(name)
        .and_then(|s| s.parse().ok())
        .unwrap_or(default)
}

let foo: String = env_or("FOO", "default_foo".into());
let bar: u64 = env_or("BAR", 1337);

If this is still too much repetition for your taste, you can abstract this further with a derive macro, to give you a struct with fields corresponding to distinct variables.

7 Likes

Do you have an example of handling environment variables that you prefer? If all you want is shortness, Bash comes to mind - however I find the syntax rather ugly and the symbols meaningless.

For example, I had to research what this pattern (getting an environmental variable and, if not present, get a default value instead) looks like in Bash. Apparently it looks like this: ${VARIABLE:-default}. I find this strange to look at, not at all representing the operation I am trying to do. Also, the dash might (imo) reasonably be interpreted as a minus sign, making logic errors harder to catch (i.e. a ${OFFSET:-1} which might be read as the default offset being negative one).

Rust handles this better, as there is a common type for the possibility of a value being absent (Option<T>), which you can then - in the general case, not just with environment variables - use associated functions like .unwrap_or(T) on. It does not need any special syntax and is - in my eyes - far easier to read than e.g. Bash.

Since you mentioned the structopt env attr, why do you want to lookup env vars yourself? It does support initializing args struct from env vars with matching name, overriding them with cli args, and specifying default values for the fallback.

Note that the structopt supports all those features in its current state, it's in maintenance mode now and future efforts are merged into the clap crate.

1 Like

Function abstracting away this process is a neat solution.

I just wanted to see what people come up with.