Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access nested structures without moving

Tags:

nested

rust

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?

like image 607
bam Avatar asked Oct 07 '16 14:10

bam


People also ask

How do you access members of a nested structure?

We can access Nested Structure in C in the following two ways: Using Normal variable. Using Pointer variable.


2 Answers

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();
                                    ^~~~~~~               ^~~~~~~~
like image 89
Matthieu M. Avatar answered Oct 10 '22 01:10

Matthieu M.


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).

like image 9
Lukas Kalbertodt Avatar answered Oct 09 '22 23:10

Lukas Kalbertodt