简体   繁体   中英

Right way to access an enum variant's field that is itself a field of a struct

I have a struct App :

struct App {
   cmd: Command
}

that owns a command of type Command :

enum Command {
   Cmd1 { flag: bool }
}

(I use StructOpt to derive a command line interface from that.)

To execute the right command I have a function like this:

impl App {
    fn execute(&mut self) {
        match &self.cmd {
            Command::Cmd1 { flag } => self.do_cmd1(*flag)
        };
    }
}

where I handle the actual execution in an extra function do_cmd1(&mut self, flag: bool) to keep execute clean. However, this does not work since in self.do_cmd1(*flag) I borrow self as mutable and also as immutable via *flag which belongs to cmd which in turn belongs to self .

My question is: What would be the proper way to access flag in do_cmd1 that respects the borrowing rules?

Clarification: I need this to also work for things like

enum Command {
    Cmd2 { text: String }
}

where the variant's field is not Copy .

If you move or copy the flag out of self before calling do_cmd1 , the borrows don't need to overlap.

    fn execute(&mut self) {
        match self.cmd {
            Command::Cmd1 { flag } => self.do_cmd1(flag),
        };
    }

The only changes are removing the & from &self.cmd and the * from *flag .

The above works because bool is a Copy type. For types that are not Copy , you will need to do some extra work to ensure the borrows do not overlap, as in these related questions:

This is a solution I have come up with, although I think there should be a better one:

Extend the Command enum in the following way:

impl Command {
    fn get_cmd1_flag(&self) -> Option<bool> {
        match &self {
            Command::Cmd1 { flag } => Some(*flag),
            _ => None
        }
    }
}

Then, alter the signature of do_cmd1 to do_cmd1(&mut self) (ie remove the flag argument). To access flag inside of do_cmd1 you can just call self.cmd.get_cmd1_flag() and handle the Option . Together with proper return types and use of the ? operator this is even quite comfortable to write.

What I dislike about this solution is that you somehow implement an own layer of type checking, which is why I think there should be a more elegant way. But at least this works.

Does do_cmd1 really need to be a method of App ?

In other words: You may be able to separate the cmd and the "other", non- cmd part (let's call it Executor ) and put them in different fields of your struct:

struct App {
   cmd: Command
   exe: Executor
}

impl App {
    fn execute(&mut self) {
        match &self.cmd {
            Command::Cmd1 { flag } => self.exe.do_cmd1(*flag)
        };
    }
}

This way, it is clear which parts of self are actually borrowed mutably.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM