Невозможно переместить значение во взаимоисключающих API-методах Entry - PullRequest
0 голосов
/ 03 апреля 2020

Я хочу сделать следующим образом, но columns перемещается как в and_modify, так и в or_insert (что, как мы знаем, взаимоисключающие), но компилятор не допустит этого. Есть ли способ по-прежнему использовать Entry API, или я должен рефакторинг на что-то вроде match self.tables.get(&table_name), что компилятор поймет, что это взаимоисключающие ветви?

self.tables.entry(table_name)
           .and_modify(|e| e.columns = Some(columns))
           .or_insert(TableInfo{
               policies: None,
               columns: Some(columns),
           });

1 Ответ

3 голосов
/ 03 апреля 2020

Поскольку or_insert() возвращает изменяемую ссылку на значение - либо существующее, либо вновь вставленное - вы должны иметь возможность вставить None для columns в новую запись, а затем безоговорочно перезаписать ее с помощью Some(columns):

self.tables
    .entry(table_name)
    .or_insert(TableInfo {
        policies: None,
        columns: None,
    })
    .columns = Some(columns);

Другое, несколько более универсальное c решение заключается в том, чтобы сначала обернуть columns в Option, а затем использовать Option::take() в двух взаимоисключающих замыканиях:

let mut columns = Some(columns);
self.tables
    .entry(table_name)
    .and_modify(|t| t.columns = columns.take())
    .or_insert_with(|| TableInfo {
        policy: None,
        columns: columns.take(),
    });

Только одно из двух замыканий сможет извлечь исходное значение, но мы знаем, что будет выполнено ровно одно, так что это нормально. (Я заменил or_insert() на or_insert_with(), поэтому у нас фактически есть два замыкания, только одно из которых выполняется. Это не обязательно, чтобы этот подход работал, но я думаю, что он делает его более понятным.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...