bam
bam

Reputation: 994

Access nested structures without moving

I've got these structs:

#[derive(Debug, RustcDecodable)]
struct Config {
    ssl: Option<SslConfig>,
}

#[derive(Debug, RustcDecodable)]
struct SslConfig {
    key: Option<String>,
    cert: Option<String>,
}

They get filled from a toml file. This works perfectly fine. Since I got an Option<T> in it I either have to call unwrap() or do a match.

But if I want to do the following:

let cfg: Config = read_config(); // Reads a File, parses it and returns the Config-Struct
let keypath = cfg.ssl.unwrap().key.unwrap();
let certpath = cfg.ssl.unwrap().cert.unwrap();

It won't work because cfg.ssl gets moved to keypath. But why does it get moved? I call unwrap() on ssl to get the key (and unwrap() it to). So the result of key.unwrap() should get moved?

Or am I missing a point? Whats the best way to make these structs accessible like this (or in a other neat way)? I tried to implement #[derive(Debug, RustcDecodable, Copy, Clone)] but this won't work because I have to implement Copy to String as well. Then I have to implement Copy to Vec<u8> and so on. There must be a more convenient solution?

Upvotes: 24

Views: 7835

Answers (2)

Matthieu M.
Matthieu M.

Reputation: 299810

What is the definition of Option::unwrap? From the documentation:

fn unwrap(self) -> T

it consumes its input (cfg.ssl here).

This is not what you want, you instead want to go from Option<T> to &T, which will start by consuming &self (by reference, not value)... or you want to clone the Option before calling unwrap.

Cloning is rarely the solution... the alternative here is as_ref:

fn as_ref(&self) -> Option<&T>

And therefore you can write:

let keypath /*: &String*/ = cfg.ssl.as_ref().unwrap().key.as_ref().unwrap();
                                    ^~~~~~~               ^~~~~~~~

Upvotes: 35

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88626

So the result of key.unwrap() should get moved?

Yes, but not only that. The key insight here is, that a variable get's moved into of unwrap(). Let's look at the function signature:

fn unwrap(self) -> T { ... }

It takes self, so the object is moved into the function. But this applies to ssl.unwrap(), too!

So when writing:

cfg.ssl.unwrap().key.unwrap();

You first move cfg.ssl into unwrap(), then you access one field of the result and move that field into unwrap() again. So yes, cfg.ssl is moved. In order to solve this, you can save the temporary result of the first unwrap() call, like so:

let ssl = cfg.ssl.unwrap();
let keypath = ssl.key.unwrap();
let certpath = ssl.cert.unwrap();

Or you can look at the as_ref() method, if you don't want to move (which is probably the case).

Upvotes: 11

Related Questions