Value used after move on configuration builder

I'm currently trying to figure out how to make this work. I'm using Rocket.rs configuration with Clap for command-line parsing. I have a situation where I only want to set TLS configuration if paths to cert/key pair are specified.

let config_builder = Config::build(Environment::Production)
                                .address("0.0.0.0")
                                .port(8000)
                                .log_level(LoggingLevel::Off);
if args.is_present("cert") && args.is_present("key") {
    config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
}
let config = config_builder.finalize().unwrap();

This results in an error:

error[E0382]: use of moved value: `config_builder`
   --> src/main.rs:102:18
    |
100 |         config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
    |         -------------- value moved here
101 |     }
102 |     let config = config_builder.finalize().unwrap();
    |                  ^^^^^^^^^^^^^^ value used here after move
    |
    = note: move occurs because `config_builder` has type `rocket::config::ConfigBuilder`, which does not implement the `Copy` trait

I've tried understanding if I can somehow mut, Rc or RefCell my way out of this problem, but with no success. Any help is appreciated.

Seems like tls() takes ownership of config_builder. I assume as a result it retrurn you a new config_builder.

So you might fix your error by reassigning the variable
let config_builder = config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());

1 Like

This changes the issue, but I'm not sure it would lead to a path I would want.

warning: unused variable: `config_builder`
   --> src/main.rs:106:13
    |
106 |         let config_builder = config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
    |             ^^^^^^^^^^^^^^ help: consider prefixing with an underscore: `_config_builder`
    |
    = note: #[warn(unused_variables)] on by default

error[E0382]: use of moved value: `config_builder`
   --> src/main.rs:108:18
    |
106 |         let config_builder = config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
    |                              -------------- value moved here
107 |     }
108 |     let config = config_builder.finalize().unwrap();
    |                  ^^^^^^^^^^^^^^ value used here after move
    |
    = note: move occurs because `config_builder` has type `rocket::config::ConfigBuilder`, which does not implement the `Copy` trait

I've found a way around the issue by not using the builder object at all. I'd still be curious if someone could solve this with Rc/Refcell. My solution for now is this:

let mut config = Config::new(Environment::Production);
    config.set_address("0.0.0.0");
    config.set_port(8000);
    config.set_log_level(LoggingLevel::Off);
    if args.is_present("cert") && args.is_present("key") {
        config.set_tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
    }

And just forget about the builder style object.

Oh sorry should have tested this :slight_smile:

Here is a Working example to the original problem

1 Like

that let works only within its block. You need a let outside, on the same level as code with let config = config_builder.

if these blocks are because of if/else, then do:

let config_builder = if … { config_builder.tls(…) } else { config_builder };

Make your builder mut and then reassign to it within the if block

let mut config_builder = Config::build(Environment::Production)
                                .address("0.0.0.0")
                                .port(8000)
                                .log_level(LoggingLevel::Off);
if args.is_present("cert") && args.is_present("key") {
    config_builder = config_builder.tls(args.value_of("cert").unwrap(), args.value_of("key").unwrap());
}
let config = config_builder.finalize().unwrap();
1 Like

This is a good read for understanding the builder pattern and it's interaction with ownership.

They say that builder methods should either all borrow self (if possible) or if one of them needs to consume self, then they all should.

The usage example for the consuming case on that page declares the builder as mut, and re-assigns it where needed, as proposed by @Yandros above.

2 Likes

Thank you to everyone who replied! Useful to know that a simple reassignment within the if can fix the issue.