summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--futures/src/subscription.rs38
2 files changed, 30 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3bd1cf2a..0d16cece 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -89,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `size_hint` not being called from `element::Map`. [#2224](https://github.com/iced-rs/iced/pull/2224)
- `size_hint` not being called from `element::Explain`. [#2225](https://github.com/iced-rs/iced/pull/2225)
- Slow touch scrolling for `TextEditor` widget. [#2140](https://github.com/iced-rs/iced/pull/2140)
+- `Subscription::map` using unreliable function pointer hash to identify mappers. [#2237](https://github.com/iced-rs/iced/pull/2237)
Many thanks to...
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index 7163248d..e32227f6 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -10,6 +10,7 @@ use crate::{BoxStream, MaybeSend};
use futures::channel::mpsc;
use futures::never::Never;
+use std::any::TypeId;
use std::hash::Hash;
/// A stream of runtime events.
@@ -88,17 +89,29 @@ impl<Message> Subscription<Message> {
}
/// Transforms the [`Subscription`] output with the given function.
- pub fn map<A>(mut self, f: fn(Message) -> A) -> Subscription<A>
+ ///
+ /// # Panics
+ /// The closure provided must be a non-capturing closure. The method
+ /// will panic in debug mode otherwise.
+ pub fn map<F, A>(mut self, f: F) -> Subscription<A>
where
Message: 'static,
+ F: Fn(Message) -> A + MaybeSend + Clone + 'static,
A: 'static,
{
+ debug_assert!(
+ std::mem::size_of::<F>() == 0,
+ "the closure {} provided in `Subscription::map` is capturing",
+ std::any::type_name::<F>(),
+ );
+
Subscription {
recipes: self
.recipes
.drain(..)
- .map(|recipe| {
- Box::new(Map::new(recipe, f)) as Box<dyn Recipe<Output = A>>
+ .map(move |recipe| {
+ Box::new(Map::new(recipe, f.clone()))
+ as Box<dyn Recipe<Output = A>>
})
.collect(),
}
@@ -143,27 +156,34 @@ pub trait Recipe {
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output>;
}
-struct Map<A, B> {
+struct Map<A, B, F>
+where
+ F: Fn(A) -> B + 'static,
+{
recipe: Box<dyn Recipe<Output = A>>,
- mapper: fn(A) -> B,
+ mapper: F,
}
-impl<A, B> Map<A, B> {
- fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: fn(A) -> B) -> Self {
+impl<A, B, F> Map<A, B, F>
+where
+ F: Fn(A) -> B + 'static,
+{
+ fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: F) -> Self {
Map { recipe, mapper }
}
}
-impl<A, B> Recipe for Map<A, B>
+impl<A, B, F> Recipe for Map<A, B, F>
where
A: 'static,
B: 'static,
+ F: Fn(A) -> B + 'static + MaybeSend,
{
type Output = B;
fn hash(&self, state: &mut Hasher) {
+ TypeId::of::<F>().hash(state);
self.recipe.hash(state);
- self.mapper.hash(state);
}
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {