1

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.

Andreas
  • 13
  • 4
  • With the "clarification", I think this is a duplicate of several other questions, such as [Cannot borrow `*self` as mutable because `self.history[..]` is also borrowed as immutable](https://stackoverflow.com/q/23032464/3650362), [How can I call a mutating method while holding a reference to self?](https://stackoverflow.com/q/27335252/3650362), and perhaps [Passing mutable self reference to method of owned object](https://stackoverflow.com/q/30681468/3650362) (that last one swaps the receiver and argument of `do_cmd1` but the suggestions of the answers apply just as well). – trent Feb 28 '20 at 14:15

3 Answers3

1

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:

trent
  • 25,033
  • 7
  • 51
  • 90
  • Thanks for your answer. You're right, this solution works for `Copy` types. It does not work for types like `String` which you might also like to have as a command parameter. I will clarify my question. – Andreas Feb 28 '20 at 13:30
  • @Andreas I have edited the answer to add links to some other questions that I think answer the question. Do these help? – trent Feb 28 '20 at 14:50
  • The two main strategies will probably be cloning the `String` inside the `match` and calling `do_cmd1` outside (so the borrows aren't live at the same time), or redefining `do_cmd1` to not borrow all of `self` (so the borrows don't refer to the same thing). – trent Feb 28 '20 at 14:53
  • 1
    Thanks for pointing out those related questions. I agree with your conclusion about the two solutions that work in this case (since using StructOpt prevents altering the types in `App` and `Command`). @phimuemue's answer goes into a similar direction. I think, I like `do_cmd1` not borrowing all of `self` best and am going to implement that. I wil accept your answer. Thanks again for the help! – Andreas Feb 28 '20 at 23:26
0

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) (i.e. 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.

Andreas
  • 13
  • 4
0

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.

phimuemue
  • 34,669
  • 9
  • 84
  • 115