Есть ли способ создать копию перечисления с обновленными значениями некоторых полей? - PullRequest
0 голосов
/ 12 января 2019

У меня есть перечисление с несколькими вариантами записи:

enum A {
    Var1 { a: i64, b: i64 },
    Var2 { c: i32, d: i32 },
}

Я хочу создать модифицированную копию такого перечисления (с различным поведением для каждого варианта). Я знаю, что могу сделать это:

match a {
    A::Var1 { a, b } => A::Var1 { a: new_a, b },
    A::Var2 { c, d } => A::Var2 { c, d: new_d },
}

Однако у каждого варианта есть довольно много полей, и я бы предпочел не передавать их явно. Есть ли способ сказать "клонировать это перечисление, , кроме , использовать это значение для поля x вместо клонированного значения"?

Ответы [ 2 ]

0 голосов
/ 12 января 2019

Не совсем. Существует «функциональный синтаксис обновления записи», но он только для struct s:

struct Foo {
    bar: u8,
    baz: u8,
    quux: u8,
}

fn foo() {
    let foobar = Foo {
        bar: 1,
        baz: 2,
        quux: 3,
    };
    let bazquux = Foo { baz: 4, ..foobar };
}

Лучшее, что вы можете сделать, не создавая структуры для каждого варианта, примерно так:

let mut altered = x.clone();
match &mut altered {
    A::Var1 { a, .. } => *a = new_a,
    A::Var2 { d, .. } => *d = new_d,
};
altered
0 голосов
/ 12 января 2019

Боюсь, что вы нарушаете одно из ограничений для Rust, основанное на его дизайне, и ваше единственное реальное решение - это мутация на месте и написание функции-мутатора или четырех.

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

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

Рассмотрим, например, очень надуманный пример. Мы создаем приложение, которое должно работать с разными предметами и облагать их налогами. Указанные налоги зависят от типа продуктов, и по какой-то причине все наши продукты представлены вариантами enum, например: # [Вывести (отладки)] enum Item { Еда {цена: у8, калории: у8}, Технология {цена: у8}, }

trait TaxExt {
    fn apply_tax(&mut self);
}
impl TaxExt for Item {
    fn apply_tax(&mut self) {
        match self {
            &mut Item::Food {
                ref mut price,
                calories: _,
            } => {
                // Our food costs double, for tax reasons
                *price *= 2;
            }
            &mut Item::Technology { ref mut price } => {
                // Technology has a 1 unit tax
                *price += 1;
            }
        }
    }
}

fn main() {
    let mut obj = Item::Food {
        price: 3,
        calories: 200,
    };
    obj.apply_tax();
    println!("{:?}", obj);
}

детская площадка

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

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