Как одновременно разрушить поля перечисления и целое значение?

#enums #rust

#перечисления #Ржавчина

Вопрос:

Я хочу сопоставить вариант перечисления, обработать его, а затем передать его дальше. Код должен выглядеть следующим образом:

 enum Event {
    WindowEvent {
        event: WindowEvent,
        window_id: usize,
    },
}
enum WindowEvent {
    Resized(u8),
}

struct Demo {
    size: u8,
}

impl Demo {
    fn reproduction(amp;mut self, event: Event) {
        match event {
            e
            @
            Event::WindowEvent {
                event: WindowEvent::Resized(size),
                ..
            } => {
                self.size = size;
                self.handle_event(e);
            }
        }
    }

    fn handle_event(amp;self, _event: Event) {}
}
  

Этот код не проходит проверку заимствования:

 error[E0007]: cannot bind by-move with sub-bindings
  --> src/lib.rs:18:13
   |
18 | /             e
19 | |             @
20 | |             Event::WindowEvent {
21 | |                 event: WindowEvent::Resized(size),
22 | |                 ..
23 | |             } => {
   | |_____________^ binds an already bound by-move value by moving it

error[E0658]: pattern bindings after an `@` are unstable
  --> src/lib.rs:21:45
   |
21 |                 event: WindowEvent::Resized(size),
   |                                             ^^^^
   |
   = note: see issue #65490 <https://github.com/rust-lang/rust/issues/65490> for more information

error[E0382]: use of moved value: `event`
  --> src/lib.rs:21:45
   |
16 |       fn reproduction(amp;mut self, event: Event) {
   |                                  ----- move occurs because `event` has type `Event`, which does not implement the `Copy` trait
17 |           match event {
18 | /             e
19 | |             @
20 | |             Event::WindowEvent {
21 | |                 event: WindowEvent::Resized(size),
   | |                                             ^^^^ value used here after move
22 | |                 ..
23 | |             } => {
   | |_____________- value moved here
  

Я могу полностью разрушить event и создать его снова при передаче handle_event , но это кажется неправильным. Зачем мне создавать новый объект, когда я могу передать существующий? Почему я должен заботиться о других полях (их может быть много!), Когда я мог бы использовать подстановочный знак .. :

 match event {
    Event::WindowEvent {
        event: WindowEvent::Resized(size),
        window_id,
    } => {
        self.size = size;
        self.handle_event(Event::WindowEvent {
            event: WindowEvent::Resized(size),
            window_id,
        });
    }
}
  

Я нашел другое решение, которое требует сопоставления дважды:

 match event {
    e
    @
    Event::WindowEvent {
        event: WindowEvent::Resized(_),
        ..
    } => {
        if let Event::WindowEvent {
            event: WindowEvent::Resized(size),
            ..
        } = e
        {
            self.size = size;
        }
        self.handle_event(e);
    }
}
  

Есть ли лучший способ деструктурировать вариант перечисления без потери исходного значения?

Комментарии:

1. Мне кажется, вашу проблему можно было бы решить, просто написав self.handle_event(event) и e вообще не пытаясь связать. Устраняет ли это проблему? Если нет, нам понадобится больше деталей.

2. После попытки заполнить пробелы в вашем коде это работает , поэтому совершенно неясно, в чем проблема.

3. Спасибо! «#! [feature(bindings_after_at)]» — это именно то, что я искал

Ответ №1:

В этом случае повторно event используйте значение, не пытаясь использовать причудливые методы сопоставления с образцом:

 match event {
    Event::WindowEvent {
        event: WindowEvent::Resized(size),
        ..
    } => {
        self.size = size;
        self.handle_event(event);
    }
}
  

В будущем будет другой вариант. Как указано в сообщениях об ошибках компилятора:

ошибка [E0007]: невозможно выполнить привязку с помощью вложенных привязок

ошибка [E0658]: привязки шаблонов после @ нестабильны

примечание: для получения дополнительной информации см. Выпуск # 65490

В nightly Rust вы можете добавлять #![feature(bindings_after_at)] , а затем изменять свой код, чтобы он соответствовал ссылке:

 fn reproduction(amp;mut self, event: Event) {
    match amp;event {
        e
        @
        Event::WindowEvent {
            event: WindowEvent::Resized(size),
            ..
        } => {
            self.size = *size;
            self.handle_event(e);
        }
    }
}

fn handle_event(amp;self, _event: amp;Event) {}
  

Если вы сопоставляете перечисления Copy , вам не нужно будет брать ссылку.