-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Relax the Clone bound on Message types #418
Conversation
Ah crap, didn't see those. Thanks. |
I see that your opinion on this is that the user should nest cloneable messages in their own types, so I'll close the PR. Still, I'd like this to be one more indication that perhaps the Clone bound is not user-friendly, and that nesting is not an obvious solution. |
I believe the implementation details and needs of a particular part of the codebase shouldn't get to decide what a shared concept of the architecture is or isn't. In particular, it seems you are proposing a The main issue here is that the statement "a message should never be cloneable" is completely against the event sourcing paradigm of The Elm Architecture, which In event sourcing, all messages (or events) are made of pure immutable data. As a consequence, they can be freely reused, copied, stored, serialized, and deserialized. Moreover, the current state of an Thanks to this, Elm has an awesome time-travelling debugger with import/export features.
If a type isn't
I believe this is a case where the architecture of the library forces you to improve the boundaries between the different parts of your codebase. It is much better to deal with any library errors right where they happen (the async call) and map them to meaningful application errors, instead of polluting your If we want to perform a login request with
My opinion is that all messages should be able to implement |
@hecrj I feel that the disconnect, and part of the reason why you've been seeing a few people complain about the The reverse is also true, then: when I create a (to be clear, I'm not trying to advocate for a !Clone bound any more, your explanation made perfect sense)
These are important insights for newcomers, along with the patterns of splitting the That said, I've been really enjoying working with |
Yes, but this is completely applicable to messages. For instance, a bunch of
Wouldn't you ask the same question if the API asked for a closure with no arguments? It'd personally strike me as even more odd. The
I guess that's the issue. There is nothing unique about a message. When you press the same button twice, the same message is produced twice, just as if it were cloned. In fact, there should be no way for you to know where the message is even coming from. |
I have plans to start building an open-source application with |
I was referring to individual instances of a type, not e.g. an enum variant in general. Meaning: if I press a button twice, two messages are created, each are unique (though they are of the same type/variant), and two messages arrive. If that type isn't Clone, I can trust that the framework won't turn those two messages into four, for whatever reason. You're right about the closure with no arguments producing the same message over and over, though note that the closure could move other objects inside it. In a fictional scenario a closure could return a different message every time it was called. But again, this is besides the point. I understood the Elm approach and how it maps to Rust here.
Look forward to seeing it! |
How are they unique if they are not different? You could say they are different instances in memory, but that distinction is hardly useful when dealing with messages. Imagine you used
Why is this important when messages are simple events? The closure approach doesn't guarantee it will produce only two messages either. You still have to trust the framework to call your closure only when the button is pressed, same as Even if we removed the What kind of trust are you referring to?
Only an |
No need for #[derive(Debug)]
enum Message {
TypeOne,
TypeTwo,
}
fn some_framework_method<F: Fn() -> Message>(closure: F) {
println!("{:?}", (closure)() );
}
fn main() {
some_framework_method(|| {
let now = std::time::SystemTime::now();
if now.elapsed().unwrap().as_secs().is_power_of_two() {
Message::TypeOne
} else {
Message::TypeTwo
}
});
}
|
@abreis Sure, Rust isn't pure. You could also use a The whole point of the library is to guide you towards better practices.
I understand. But I still fail to see why would you ever care that a Additionally, |
I am guessing part of the issue is that the reason of the However, I believe you are the first person to bring this up. Most of the discussions around
|
@hecrj I'm new to Iced and just ran into this exact issue, went to check the type of The reason I haven't used |
I've found that the
Clone
bound onMessage
types can be a hit to ergonomics, particularly when working withCommand
s.Example: a typical fallible async function returns some form of
Result<_, Error>
. The standard approach to call such a function in iced seems to be:and then handle the message in the view's
update()
.However, most error types are not
Clone
(e.g.,std::io::Error
), so that future's output can't be wrapped in a Message directly and some mapping of the error to a clonable type is needed.I've been going through the code in
iced_native
and from what I can see theClone
bound is only required byTextInput
andButton
, for code such as:From what I understand the aim of this is to produce a new message on each key press. This PR proposes changing these fields from:
to
i.e., lazily producing the Message variant. This allows us to get rid of the
Clone
bound.I've opened this as a draft to hear your thoughts first. I haven't adjusted documentation nor the web versions of
TextInput
/Button
. What do you think?