An Ergonomic Approach to Configuration in Rust

Ergonomic Configuration

In most of the programs I have written so far the configuration consists of a combination of defaults, optional settings from configuration files, and command line options, in that order of precedence.

And once established, it is usually immutable.

Setting this up is not difficult, but it helps to have a worked example, as otherwise it is easy to get lost in what to call things.

I have created a worked example which has the following features:

  • Uses lazy_static to create a global, singleton Configuration struct which is initialized with the merging of all the different possible sources of configuration.
  • Because it is global, you don’t need to pass it down through method chains.
  • If the command line options are bad, the program terminates, thanks to clap.
  • The Configuration in this example is immutable, which makes for a safe singleton. This works for most of my programs. If you want some mutability, consider interior mutability.
  • This approach lends itself to decomposition - there is no need to have just one big Configuration struct, you could not have several dealing with different concerns.

The downside of ease-of-use

I call it ergonomic because it removes the need to pollute your method signatures with &Configuration parameters. The downside to this is that your methods now depend on ambient context which you can’t tell just from looking at your method signatures, you need to inspect the code inside to discover this (according to the principles of Dependency Injection this is a bad thing and your method signatures or constructors should always make your dependencies apparent). However for some kinds of programs it is a reasonable trade-off.

If this bothers you, you can still use the worked example as a starting point. Just remove the lazy_static and return the Configuration object and bind it to a local variable at the beginning of main:

fn main() {
    let config = Configuration::new();
    /// Now pass &config, or sub-parts of config, into the methods that need them.
}
comments powered by Disqus