Правильный способ доступа к полю варианта enum, которое само является полем структуры - PullRequest
1 голос
/ 28 февраля 2020

У меня есть структура App:

struct App {
   cmd: Command
}

, которой принадлежит команда типа Command:

enum Command {
   Cmd1 { flag: bool }
}

(я использую StructOpt для получения интерфейса командной строки из это.)

Чтобы выполнить правильную команду, у меня есть такая функция:

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

, где я обрабатываю фактическое выполнение в дополнительной функции do_cmd1(&mut self, flag: bool), чтобы сохранить execute в чистоте. Однако это не работает, поскольку в self.do_cmd1(*flag) я заимствую self как изменяемый, а также как неизменяемый через *flag, который принадлежит cmd, который, в свою очередь, принадлежит self.

Мой вопрос : Каков был бы правильный способ доступа к flag в do_cmd1, который уважает правила заимствования?

Уточнение: мне нужно, чтобы это также работало для таких вещей, как

enum Command {
    Cmd2 { text: String }
}

, где поле варианта не Copy.

Ответы [ 3 ]

1 голос
/ 28 февраля 2020

Если вы передвинете или скопируете флаг из self перед вызовом do_cmd1, заимствования не должны перекрываться.

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

Единственные изменения - удаление & из &self.cmd и * из *flag.

Вышеописанное работает, потому что bool относится к типу Copy. Для типов, которые не являются Copy, вам нужно будет выполнить дополнительную работу, чтобы убедиться, что заимствования не перекрываются, как в следующих связанных вопросах:

0 голосов
/ 28 февраля 2020

Действительно ли do_cmd1 должен быть методом App?

Другими словами: вы можете разделить cmd и "другую", не cmd часть (давайте назовем это Executor) и поместим их в разные поля вашей структуры:

struct App {
   cmd: Command
   exe: Executor
}

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

Таким образом, становится ясно, какие части self являются на самом деле , заимствованными в порядке изменчивости.

0 голосов
/ 28 февраля 2020

Это решение, которое я придумала, хотя я думаю, что должно быть лучшее:

Расширить перечисление Command следующим образом:

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

Тогда измените подпись от do_cmd1 до do_cmd1(&mut self) (то есть удалите аргумент flag). Чтобы получить доступ к flag внутри do_cmd1, вы можете просто позвонить self.cmd.get_cmd1_flag() и обработать Option. Вместе с правильными типами возвращаемых данных и использованием оператора ? это даже довольно удобно писать.

Что мне не нравится в этом решении, так это то, что вы каким-то образом реализуете собственный уровень проверки типов, поэтому я думаю, должен быть более элегантный способ. Но, по крайней мере, это работает.

...