antoyo / relm

Idiomatic, GTK+-based, GUI library, inspired by Elm, written in Rust
MIT License
2.41k stars 79 forks source link

Compilation error when using non-Copy types in messages #303

Closed msrd0 closed 9 months ago

msrd0 commented 1 year ago

Whenever I have a message type that is non-Copy, e.g.

#[derive(Msg)]
pub enum Msg {
    MyEvent(String)
}

Then I cannot use this message, or otherwise I get a compilation error:

use my_widget::{MyWidget, Msg::MyEvent as MyWidgetMyEvent};

view! {
    // ...
    MyWidget() {
        MyWidgetMyEvent(foo) => Msg::MyEvent(foo)
    }
    // ...
}

The error message is

error[E0507]: cannot move out of `msg` as enum variant `MyEvent` which is behind a shared reference
   --> src/ui/mod.rs:XXX:Y
    |
XXX |                 MyWidget() {
    |                 ^^^^^^^^ help: consider removing the `&`: `$message`
...
XXX |                     MyWidgetMyEvent(foo) => Msg::MyEvent(foo)
    |                                     ---  data moved here

This is impossible to fix in user code, using e.g. Msg::MyEvent(foo.clone()) or Msg::MyEvent((&*foo).into()) produces the exact same error message. The problem is within the macro-generated code that produces &MyWidgetMyEvent(foo). I believe this could be fixed by emiting &MyWidgetMyEvent(ref foo), but I haven't tested that.

antoyo commented 1 year ago

You have to add ref before foo in MyWidgetMyEvent(foo) like in MyWidgetMyEvent(ref foo).

See this example.

msrd0 commented 1 year ago

Is there a reason why you force the & in the relm::connect_stream! macro? It seems to compile just fine when the & is removed.

antoyo commented 1 year ago

I'm not sure exactly where & you're talking about, but I think I understand your question anyway.

Both the current widget and parent widgets can see the same message. The current widget gets an owned version of the event, but to avoid a clone, the parent widgets get a reference.

msrd0 commented 1 year ago

I'm talking about this one: https://docs.rs/relm/0.24.0/src/relm/macros.rs.html#113. It is the source for the &$message part in the error message.

As far as I understand it, the parameter passed by observe is already borrowed, so match msg will match a reference anyways. I tried removing the & and at least for my code, it works without any problems and I don't need the ref. Compiling all your tests is really annoying as all of them compile into their own target folder which takes forever, so I didn't try.

antoyo commented 1 year ago

Oh, that's because of match ergonomics: the Rust compiler automatically adds the & and ref for you.

I find this leads to worse error messages which is why I prefer to avoid it.

msrd0 commented 1 year ago

Well, it doesn't add the ref if you manually add the &, so I feel like right now it creates error messages where it would otherwise compile.

There might be other cases where this isn't the case that I just haven't found yet of course.

antoyo commented 1 year ago

Well, it doesn't add the ref if you manually add the &, so I feel like right now it creates error messages where it would otherwise compile.

Indeed, Rust won't do match ergonomics if you add * or &. That's the reason & is used in the macro: I wanted to disable match ergonomics.