diff --git a/components/datetime/src/provider/date_time.rs b/components/datetime/src/provider/date_time.rs index 5116cb3701e..702dd06ba47 100644 --- a/components/datetime/src/provider/date_time.rs +++ b/components/datetime/src/provider/date_time.rs @@ -93,13 +93,10 @@ where D: ResourceProvider + ?Sized, { let patterns_data = time_patterns_data_payload(data_provider, locale)?; - Ok(patterns_data.map_project_with_capture( - (length, preferences), - |data, (length, preferences), _| { - let pattern = pattern_for_time_length_inner(data, length, &preferences).clone(); - pattern.into() - }, - )) + Ok(patterns_data.map_project(|data, _| { + let pattern = pattern_for_time_length_inner(data, length, &preferences).clone(); + pattern.into() + })) } fn date_patterns_data_payload( @@ -128,12 +125,10 @@ where D: ResourceProvider + ?Sized, { let patterns_data = date_patterns_data_payload(data_provider, locale)?; - Ok( - patterns_data.map_project_with_capture(length, |data, length, _| { - let pattern = pattern_for_date_length_inner(data, length); - pattern.into() - }), - ) + Ok(patterns_data.map_project(|data, _| { + let pattern = pattern_for_date_length_inner(data, length); + pattern.into() + })) } /// Determine the appropriate `Pattern` for a given `options::length::Date` bag. @@ -146,18 +141,16 @@ where D: ResourceProvider + ?Sized, { let patterns_data = date_patterns_data_payload(data_provider, locale)?; - Ok( - patterns_data.map_project_with_capture(length, |data, length, _| { - let pattern = match length { - length::Date::Full => data.length_combinations.full, - length::Date::Long => data.length_combinations.long, - length::Date::Medium => data.length_combinations.medium, - length::Date::Short => data.length_combinations.short, - }; + Ok(patterns_data.map_project(|data, _| { + let pattern = match length { + length::Date::Full => data.length_combinations.full, + length::Date::Long => data.length_combinations.long, + length::Date::Medium => data.length_combinations.medium, + length::Date::Short => data.length_combinations.short, + }; - pattern.into() - }), - ) + pattern.into() + })) } pub struct PatternSelector<'a, D: ?Sized> { @@ -231,30 +224,27 @@ where let time_patterns_data = time_patterns_data_payload(self.data_provider, self.locale)?; let date_patterns_data = date_patterns_data_payload(self.data_provider, self.locale)?; - date_patterns_data.try_map_project_with_capture( - (date_length, time_length, preferences, time_patterns_data), - |data, (date_length, time_length, preferences, time_patterns_data), _| { - // TODO (#1131) - We may be able to remove the clone here. - let date = pattern_for_date_length_inner(data.clone(), date_length) - .expect_pattern("Lengths are single patterns"); + date_patterns_data.try_map_project(|data, _| { + // TODO (#1131) - We may be able to remove the clone here. + let date = pattern_for_date_length_inner(data.clone(), date_length) + .expect_pattern("Lengths are single patterns"); - let pattern = match date_length { - length::Date::Full => data.length_combinations.full, - length::Date::Long => data.length_combinations.long, - length::Date::Medium => data.length_combinations.medium, - length::Date::Short => data.length_combinations.short, - }; + let pattern = match date_length { + length::Date::Full => data.length_combinations.full, + length::Date::Long => data.length_combinations.long, + length::Date::Medium => data.length_combinations.medium, + length::Date::Short => data.length_combinations.short, + }; - // TODO (#1131) - We may be able to remove the clone here. - let time = pattern_for_time_length_inner( - time_patterns_data.get().clone(), - time_length, - &preferences, - ) - .expect_pattern("Lengths are single patterns"); - Ok(PatternPlurals::from(pattern.combined(date, time)?).into()) - }, - ) + // TODO (#1131) - We may be able to remove the clone here. + let time = pattern_for_time_length_inner( + time_patterns_data.get().clone(), + time_length, + &preferences, + ) + .expect_pattern("Lengths are single patterns"); + Ok(PatternPlurals::from(pattern.combined(date, time)?).into()) + }) } /// Determine the appropriate `PatternPlurals` for a given `options::components::Bag`. diff --git a/components/datetime/src/raw/datetime.rs b/components/datetime/src/raw/datetime.rs index 2bd3847db00..0b442a3583c 100644 --- a/components/datetime/src/raw/datetime.rs +++ b/components/datetime/src/raw/datetime.rs @@ -358,14 +358,12 @@ impl DateTimeFormat { date: DateFormat, time: TimeFormat, ) -> Result { + let generic_pattern = &date.generic_pattern; + let time_patterns = &time.patterns; let patterns = date .patterns - .try_map_project_with_capture::, - DataPayload, - ), DateTimeFormatError>( - (date.generic_pattern, time.patterns), - |data, (generic_pattern, time_patterns), _| { + .try_map_project::( + |data, _| { let date_pattern = data.0.expect_pattern("Lengths are single patterns"); let time_pattern: crate::pattern::runtime::Pattern = time_patterns .get() diff --git a/provider/blob/src/blob_data_provider.rs b/provider/blob/src/blob_data_provider.rs index dc1ff76d827..c529383bf81 100644 --- a/provider/blob/src/blob_data_provider.rs +++ b/provider/blob/src/blob_data_provider.rs @@ -89,24 +89,21 @@ impl BufferProvider for BlobDataProvider { Ok(DataResponse { metadata, payload: Some(DataPayload::from_yoked_buffer( - self.data.try_map_project_cloned_with_capture( - (key, req), - |blob, (key, req), _| { - let idx = blob - .keys - .get0(&key.get_hash()) - .ok_or(DataErrorKind::MissingResourceKey) - .and_then(|cursor| { - cursor - .get1_copied_by(|bytes| req.options.strict_cmp(bytes).reverse()) - .ok_or(DataErrorKind::MissingResourceOptions) - }) - .map_err(|kind| kind.with_req(key, req))?; - blob.buffers.get(idx).ok_or_else(|| { - DataError::custom("Invalid blob bytes").with_req(key, req) + self.data.try_map_project_cloned(|blob, _| { + let idx = blob + .keys + .get0(&key.get_hash()) + .ok_or(DataErrorKind::MissingResourceKey) + .and_then(|cursor| { + cursor + .get1_copied_by(|bytes| req.options.strict_cmp(bytes).reverse()) + .ok_or(DataErrorKind::MissingResourceOptions) }) - }, - )?, + .map_err(|kind| kind.with_req(key, req))?; + blob.buffers + .get(idx) + .ok_or_else(|| DataError::custom("Invalid blob bytes").with_req(key, req)) + })?, )), }) } diff --git a/provider/core/src/data_provider.rs b/provider/core/src/data_provider.rs index c6f63b10ebd..0f3dce4680b 100644 --- a/provider/core/src/data_provider.rs +++ b/provider/core/src/data_provider.rs @@ -260,6 +260,11 @@ where /// This function is similar to [`DataPayload::try_from_rc_buffer`], but it accepts a buffer /// that is already yoked. /// + /// The callback takes an additional `PhantomData<&()>` parameter to anchor lifetimes + /// (see [#86702](https://github.com/rust-lang/rust/issues/86702)) This parameter + /// should just be ignored in the callback. + /// + /// /// # Examples /// /// ``` @@ -270,8 +275,7 @@ where /// /// let payload = DataPayload::::try_from_yoked_buffer( /// Yoke::attach_to_cart("{\"message\":\"Hello World\"}".as_bytes().into(), |b| b), - /// (), - /// |bytes, _, _| serde_json::from_slice(bytes), + /// |bytes, _| serde_json::from_slice(bytes), /// ) /// .expect("JSON is valid"); /// @@ -279,18 +283,17 @@ where /// # } // feature = "serde_json" /// ``` #[allow(clippy::type_complexity)] - pub fn try_from_yoked_buffer( + pub fn try_from_yoked_buffer( yoked_buffer: Yoke<&'static [u8], RcWrap>, - capture: T, - f: for<'de> fn( + f: F, + ) -> Result + where + F: for<'de> FnOnce( <&'static [u8] as yoke::Yokeable<'de>>::Output, - T, PhantomData<&'de ()>, ) -> Result<>::Output, E>, - ) -> Result { - let yoke = yoked_buffer - .wrap_cart_in_option() - .try_map_project_with_capture(capture, f)?; + { + let yoke = yoked_buffer.wrap_cart_in_option().try_map_project(f)?; Ok(Self { yoke }) } @@ -399,8 +402,8 @@ where /// data from its context. Use one of the sister methods if you need these capabilities: /// /// - [`DataPayload::map_project_cloned()`] if you don't have ownership of `self` - /// - [`DataPayload::map_project_with_capture()`] to pass context to the mapping function - /// - [`DataPayload::map_project_cloned_with_capture()`] to do both of these things + /// - [`DataPayload::try_map_project()`] to bubble up an error + /// - [`DataPayload::try_map_project_cloned()`] to do both of the above /// /// # Examples /// @@ -431,15 +434,13 @@ where /// assert_eq!("Hello World", p2.get()); /// ``` #[allow(clippy::type_complexity)] - pub fn map_project( - self, - f: for<'a> fn( + pub fn map_project(self, f: F) -> DataPayload + where + M2: DataMarker, + F: for<'a> FnOnce( >::Output, PhantomData<&'a ()>, ) -> >::Output, - ) -> DataPayload - where - M2: DataMarker, { DataPayload { yoke: self.yoke.map_project(f), @@ -475,124 +476,20 @@ where /// assert_eq!(p1.get().message, *p2.get()); /// ``` #[allow(clippy::type_complexity)] - pub fn map_project_cloned<'this, M2>( - &'this self, - f: for<'a> fn( - &'this >::Output, - PhantomData<&'a ()>, - ) -> >::Output, - ) -> DataPayload - where - M2: DataMarker, - { - DataPayload { - yoke: self.yoke.map_project_cloned(f), - } - } - - /// Version of [`DataPayload::map_project()`] that moves `self` and takes a `capture` - /// parameter to pass additional data to `f`. - /// - /// # Examples - /// - /// Capture a string from the context and append it to the message: - /// - /// ``` - /// // Same imports and definitions as above - /// # use icu_provider::hello_world::*; - /// # use icu_provider::prelude::*; - /// # use std::borrow::Cow; - /// # struct HelloWorldV1MessageMarker; - /// # impl DataMarker for HelloWorldV1MessageMarker { - /// # type Yokeable = Cow<'static, str>; - /// # } - /// - /// let p1: DataPayload = DataPayload::from_owned(HelloWorldV1 { - /// message: Cow::Borrowed("Hello World"), - /// }); - /// - /// assert_eq!("Hello World", p1.get().message); - /// - /// let p2: DataPayload = - /// p1.map_project_with_capture("Extra", |mut obj, capture, _| { - /// obj.message.to_mut().push_str(capture); - /// obj.message - /// }); - /// - /// assert_eq!("Hello WorldExtra", p2.get()); - /// ``` - #[allow(clippy::type_complexity)] - pub fn map_project_with_capture( - self, - capture: T, - f: for<'a> fn( - >::Output, - capture: T, - PhantomData<&'a ()>, - ) -> >::Output, - ) -> DataPayload + pub fn map_project_cloned<'this, M2, F>(&'this self, f: F) -> DataPayload where M2: DataMarker, - { - DataPayload { - yoke: self.yoke.map_project_with_capture(capture, f), - } - } - - /// Version of [`DataPayload::map_project()`] that borrows `self` and takes a `capture` - /// parameter to pass additional data to `f`. - /// - /// # Examples - /// - /// Same example as above, but this time, do not move out of `p1`: - /// - /// ``` - /// // Same imports and definitions as above - /// # use icu_provider::hello_world::*; - /// # use icu_provider::prelude::*; - /// # use std::borrow::Cow; - /// # struct HelloWorldV1MessageMarker; - /// # impl DataMarker for HelloWorldV1MessageMarker { - /// # type Yokeable = Cow<'static, str>; - /// # } - /// - /// let p1: DataPayload = DataPayload::from_owned(HelloWorldV1 { - /// message: Cow::Borrowed("Hello World"), - /// }); - /// - /// assert_eq!("Hello World", p1.get().message); - /// - /// let p2: DataPayload = - /// p1.map_project_cloned_with_capture("Extra", |obj, capture, _| { - /// let mut message = obj.message.clone(); - /// message.to_mut().push_str(capture); - /// message - /// }); - /// - /// // Note: p1 is still valid, but the values no longer equal. - /// assert_ne!(p1.get().message, *p2.get()); - /// assert_eq!("Hello WorldExtra", p2.get()); - /// ``` - #[allow(clippy::type_complexity)] - pub fn map_project_cloned_with_capture<'this, M2, T>( - &'this self, - capture: T, - f: for<'a> fn( + F: for<'a> FnOnce( &'this >::Output, - capture: T, PhantomData<&'a ()>, ) -> >::Output, - ) -> DataPayload - where - M2: DataMarker, { DataPayload { - yoke: self.yoke.map_project_cloned_with_capture(capture, f), + yoke: self.yoke.map_project_cloned(f), } } - /// Version of [`DataPayload::map_project()`] that moves `self`, takes a `capture` - /// parameter to pass additional data to `f`, and bubbles up an error from `f`. + /// Version of [`DataPayload::map_project()`] that bubbles up an error from `f`. /// /// # Examples /// @@ -614,12 +511,13 @@ where /// /// assert_eq!("Hello World", p1.get().message); /// + /// let string_to_append = "Extra"; /// let p2: DataPayload = - /// p1.try_map_project_with_capture("Extra", |mut obj, capture, _| { + /// p1.try_map_project(|mut obj, _| { /// if obj.message.is_empty() { /// return Err("Example error"); /// } - /// obj.message.to_mut().push_str(&capture); + /// obj.message.to_mut().push_str(string_to_append); /// Ok(obj.message) /// })?; /// @@ -627,25 +525,20 @@ where /// # Ok::<(), &'static str>(()) /// ``` #[allow(clippy::type_complexity)] - pub fn try_map_project_with_capture( - self, - capture: T, - f: for<'a> fn( + pub fn try_map_project(self, f: F) -> Result, E> + where + M2: DataMarker, + F: for<'a> FnOnce( >::Output, - capture: T, PhantomData<&'a ()>, ) -> Result<>::Output, E>, - ) -> Result, E> - where - M2: DataMarker, { Ok(DataPayload { - yoke: self.yoke.try_map_project_with_capture(capture, f)?, + yoke: self.yoke.try_map_project(f)?, }) } - /// Version of [`DataPayload::map_project()`] that borrows `self`, takes a `capture` - /// parameter to pass additional data to `f`, and bubbles up an error from `f`. + /// Version of [`DataPayload::map_project_cloned()`] that bubbles up an error from `f`. /// /// # Examples /// @@ -667,13 +560,14 @@ where /// /// assert_eq!("Hello World", p1.get().message); /// + /// let string_to_append = "Extra"; /// let p2: DataPayload = - /// p1.try_map_project_cloned_with_capture("Extra", |obj, capture, _| { + /// p1.try_map_project_cloned(|obj, _| { /// if obj.message.is_empty() { /// return Err("Example error"); /// } /// let mut message = obj.message.clone(); - /// message.to_mut().push_str(capture); + /// message.to_mut().push_str(string_to_append); /// Ok(message) /// })?; /// @@ -683,20 +577,16 @@ where /// # Ok::<(), &'static str>(()) /// ``` #[allow(clippy::type_complexity)] - pub fn try_map_project_cloned_with_capture<'this, M2, T, E>( - &'this self, - capture: T, - f: for<'a> fn( + pub fn try_map_project_cloned<'this, M2, F, E>(&'this self, f: F) -> Result, E> + where + M2: DataMarker, + F: for<'a> FnOnce( &'this >::Output, - capture: T, PhantomData<&'a ()>, ) -> Result<>::Output, E>, - ) -> Result, E> - where - M2: DataMarker, { Ok(DataPayload { - yoke: self.yoke.try_map_project_cloned_with_capture(capture, f)?, + yoke: self.yoke.try_map_project_cloned(f)?, }) } diff --git a/provider/core/src/serde/mod.rs b/provider/core/src/serde/mod.rs index 19284c6699e..00fe780bf9a 100644 --- a/provider/core/src/serde/mod.rs +++ b/provider/core/src/serde/mod.rs @@ -16,7 +16,6 @@ pub mod borrow_de_utils; use crate::buf::BufferFormat; use crate::buf::BufferProvider; use crate::prelude::*; -use core::marker::PhantomData; use serde::de::Deserialize; use yoke::trait_hack::YokeTraitHack; use yoke::Yokeable; @@ -43,7 +42,6 @@ fn deserialize_impl<'data, M>( // Allow `bytes` to be unused in case all buffer formats are disabled #[allow(unused_variables)] bytes: &'data [u8], buffer_format: BufferFormat, - _: PhantomData<&'data ()>, ) -> Result<>::Output, DataError> where M: DataMarker, @@ -96,7 +94,7 @@ impl DataPayload { // Necessary workaround bound (see `yoke::trait_hack` docs): for<'de> YokeTraitHack<>::Output>: Deserialize<'de>, { - self.try_map_project_with_capture(buffer_format, deserialize_impl::) + self.try_map_project(|bytes, _| deserialize_impl::(bytes, buffer_format)) } } diff --git a/utils/yoke/src/yoke.rs b/utils/yoke/src/yoke.rs index ef94aff8063..0761ecb06cc 100644 --- a/utils/yoke/src/yoke.rs +++ b/utils/yoke/src/yoke.rs @@ -546,12 +546,9 @@ impl Yokeable<'a>, C> Yoke { /// looking at a subfield, and producing a new yoke. This will move cart, and the provided /// transformation is only allowed to use data known to be borrowed from the cart. /// - /// This takes an additional `PhantomData<&()>` parameter as a workaround to the issue - /// described in [#86702](https://github.com/rust-lang/rust/issues/86702). This parameter - /// should just be ignored in the function. - /// - /// To capture data and pass it to the closure, use [`Yoke::map_project_with_capture()`]. - /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061). + /// The callback takes an additional `PhantomData<&()>` parameter to anchor lifetimes + /// (see [#86702](https://github.com/rust-lang/rust/issues/86702)) This parameter + /// should just be ignored in the callback. /// /// This can be used, for example, to transform data from one format to another: /// @@ -610,15 +607,13 @@ impl Yokeable<'a>, C> Yoke { /// ``` // // Safety docs can be found below on `__project_safety_docs()` - pub fn map_project

( - self, - f: for<'a> fn( + pub fn map_project(self, f: F) -> Yoke + where + P: for<'a> Yokeable<'a>, + F: for<'a> FnOnce( >::Output, PhantomData<&'a ()>, ) ->

>::Output, - ) -> Yoke - where - P: for<'a> Yokeable<'a>, { let p = f(self.yokeable.transform_owned(), PhantomData); Yoke { @@ -632,16 +627,14 @@ impl Yokeable<'a>, C> Yoke { /// /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`] /// because then it will not clone fields that are going to be discarded. - pub fn map_project_cloned<'this, P>( - &'this self, - f: for<'a> fn( - &'this >::Output, - PhantomData<&'a ()>, - ) ->

>::Output, - ) -> Yoke + pub fn map_project_cloned<'this, P, F>(&'this self, f: F) -> Yoke where P: for<'a> Yokeable<'a>, C: CloneableCart, + F: for<'a> FnOnce( + &'this >::Output, + PhantomData<&'a ()>, + ) ->

>::Output, { let p = f(self.get(), PhantomData); Yoke { @@ -650,12 +643,105 @@ impl Yokeable<'a>, C> Yoke { } } - /// This is similar to [`Yoke::map_project`], but it works around it not being able to - /// use `FnOnce` by using an explicit capture input. + /// This is similar to [`Yoke::map_project`], however it can also bubble up an error + /// from the callback. + /// + /// ``` + /// # use std::rc::Rc; + /// # use yoke::Yoke; + /// # use std::str::{self, Utf8Error}; + /// # + /// fn slice(y: Yoke<&'static [u8], Rc<[u8]>>) -> Result>, Utf8Error> { + /// y.try_map_project(move |bytes, _| str::from_utf8(bytes)) + /// } + /// ``` + /// + /// This can also be used to create a yoke for a subfield + /// + /// ``` + /// # use std::borrow::Cow; + /// # use yoke::{Yoke, Yokeable}; + /// # use std::mem; + /// # use std::rc::Rc; + /// # use std::str::{self, Utf8Error}; + /// # + /// // also safely implements Yokeable<'a> + /// struct Bar<'a> { + /// bytes_1: &'a [u8], + /// string_2: &'a str, + /// } + /// + /// fn map_project_string_1(bar: Yoke, Rc<[u8]>>) -> Result>, Utf8Error> { + /// bar.try_map_project(|bar, _| str::from_utf8(bar.bytes_1)) + /// } + /// + /// # + /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> { + /// # type Output = Bar<'a>; + /// # fn transform(&'a self) -> &'a Bar<'a> { + /// # self + /// # } + /// # + /// # fn transform_owned(self) -> Bar<'a> { + /// # // covariant lifetime cast, can be done safely + /// # self + /// # } + /// # + /// # unsafe fn make(from: Bar<'a>) -> Self { + /// # let ret = mem::transmute_copy(&from); + /// # mem::forget(from); + /// # ret + /// # } + /// # + /// # fn transform_mut(&'a mut self, f: F) + /// # where + /// # F: 'static + FnOnce(&'a mut Self::Output), + /// # { + /// # unsafe { f(mem::transmute(self)) } + /// # } + /// # } + /// ``` + pub fn try_map_project(self, f: F) -> Result, E> + where + P: for<'a> Yokeable<'a>, + F: for<'a> FnOnce( + >::Output, + PhantomData<&'a ()>, + ) -> Result<

>::Output, E>, + { + let p = f(self.yokeable.transform_owned(), PhantomData)?; + Ok(Yoke { + yokeable: unsafe { P::make(p) }, + cart: self.cart, + }) + } + + /// This is similar to [`Yoke::try_map_project`], however it does not move + /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`]) + /// + /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::map_project`] + /// because then it will not clone fields that are going to be discarded. + pub fn try_map_project_cloned<'this, P, F, E>(&'this self, f: F) -> Result, E> + where + P: for<'a> Yokeable<'a>, + C: CloneableCart, + F: for<'a> FnOnce( + &'this >::Output, + PhantomData<&'a ()>, + ) -> Result<

>::Output, E>, + { + let p = f(self.get(), PhantomData)?; + Ok(Yoke { + yokeable: unsafe { P::make(p) }, + cart: self.cart.clone(), + }) + } + /// This is similar to [`Yoke::map_project`], but it works around older versions + /// of Rust not being able to use `FnOnce` by using an explicit capture input. /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061). /// /// See the docs of [`Yoke::map_project`] for how this works. - pub fn map_project_with_capture( + pub fn map_project_with_explicit_capture( self, capture: T, f: for<'a> fn( @@ -674,12 +760,12 @@ impl Yokeable<'a>, C> Yoke { } } - /// This is similar to [`Yoke::map_project_cloned`], however it works around it not being able to - /// use `FnOnce` by using an explicit capture input. + /// This is similar to [`Yoke::map_project_cloned`], but it works around older versions + /// of Rust not being able to use `FnOnce` by using an explicit capture input. /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061). /// /// See the docs of [`Yoke::map_project_cloned`] for how this works. - pub fn map_project_cloned_with_capture<'this, P, T>( + pub fn map_project_cloned_with_explicit_capture<'this, P, T>( &'this self, capture: T, f: for<'a> fn( @@ -699,10 +785,13 @@ impl Yokeable<'a>, C> Yoke { } } - /// A version of [`Yoke::map_project`] that takes a capture and bubbles up an error - /// from the callback function. + /// This is similar to [`Yoke::try_map_project`], but it works around older versions + /// of Rust not being able to use `FnOnce` by using an explicit capture input. + /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061). + /// + /// See the docs of [`Yoke::try_map_project`] for how this works. #[allow(clippy::type_complexity)] - pub fn try_map_project_with_capture( + pub fn try_map_project_with_explicit_capture( self, capture: T, f: for<'a> fn( @@ -721,10 +810,13 @@ impl Yokeable<'a>, C> Yoke { }) } - /// A version of [`Yoke::map_project_cloned`] that takes a capture and bubbles up an error - /// from the callback function. + /// This is similar to [`Yoke::try_map_project_cloned`], but it works around older versions + /// of Rust not being able to use `FnOnce` by using an explicit capture input. + /// See [#1061](https://github.com/unicode-org/icu4x/issues/1061). + /// + /// See the docs of [`Yoke::try_map_project_cloned`] for how this works. #[allow(clippy::type_complexity)] - pub fn try_map_project_cloned_with_capture<'this, P, T, E>( + pub fn try_map_project_cloned_with_explicit_capture<'this, P, T, E>( &'this self, capture: T, f: for<'a> fn(