Merge pull request #2237 from iced-rs/fix/mapped-subscription-id

Use `TypeId` to identify `subscription::Map`
This commit is contained in:
Héctor Ramón 2024-02-05 22:05:14 +01:00 committed by GitHub
commit 8e76d53803
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 30 additions and 9 deletions

View file

@ -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::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) - `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) - 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... Many thanks to...

View file

@ -10,6 +10,7 @@ use crate::{BoxStream, MaybeSend};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::never::Never; use futures::never::Never;
use std::any::TypeId;
use std::hash::Hash; use std::hash::Hash;
/// A stream of runtime events. /// A stream of runtime events.
@ -88,17 +89,29 @@ impl<Message> Subscription<Message> {
} }
/// Transforms the [`Subscription`] output with the given function. /// 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 where
Message: 'static, Message: 'static,
F: Fn(Message) -> A + MaybeSend + Clone + 'static,
A: '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 { Subscription {
recipes: self recipes: self
.recipes .recipes
.drain(..) .drain(..)
.map(|recipe| { .map(move |recipe| {
Box::new(Map::new(recipe, f)) as Box<dyn Recipe<Output = A>> Box::new(Map::new(recipe, f.clone()))
as Box<dyn Recipe<Output = A>>
}) })
.collect(), .collect(),
} }
@ -143,27 +156,34 @@ pub trait Recipe {
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output>; 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>>, recipe: Box<dyn Recipe<Output = A>>,
mapper: fn(A) -> B, mapper: F,
} }
impl<A, B> Map<A, B> { impl<A, B, F> Map<A, B, F>
fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: fn(A) -> B) -> Self { where
F: Fn(A) -> B + 'static,
{
fn new(recipe: Box<dyn Recipe<Output = A>>, mapper: F) -> Self {
Map { recipe, mapper } Map { recipe, mapper }
} }
} }
impl<A, B> Recipe for Map<A, B> impl<A, B, F> Recipe for Map<A, B, F>
where where
A: 'static, A: 'static,
B: 'static, B: 'static,
F: Fn(A) -> B + 'static + MaybeSend,
{ {
type Output = B; type Output = B;
fn hash(&self, state: &mut Hasher) { fn hash(&self, state: &mut Hasher) {
TypeId::of::<F>().hash(state);
self.recipe.hash(state); self.recipe.hash(state);
self.mapper.hash(state);
} }
fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> { fn stream(self: Box<Self>, input: EventStream) -> BoxStream<Self::Output> {