Skip to content

Commit a40d8f5

Browse files
committed
fixup! More generic impl of Replacer for closures
Hide `ReplacerClosure` trait as an implementation detail and describe for which closures the `Replacer` trait is implemented in the documentation instead. Added documentation tests.
1 parent adf66a8 commit a40d8f5

File tree

1 file changed

+83
-16
lines changed

1 file changed

+83
-16
lines changed

src/regex/string.rs

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,23 +2371,27 @@ impl<'c, 'h> ExactSizeIterator for SubCaptureMatches<'c, 'h> {}
23712371

23722372
impl<'c, 'h> core::iter::FusedIterator for SubCaptureMatches<'c, 'h> {}
23732373

2374-
/// If a closure implements this for all `'a`, then it also implements
2375-
/// [`Replacer`].
2376-
pub trait ReplacerClosure<'a>
2377-
where
2378-
Self: FnMut(&'a Captures<'_>) -> <Self as ReplacerClosure<'a>>::Output,
2379-
{
2380-
/// Return type of the closure (may depend on lifetime `'a`).
2381-
type Output: AsRef<str>;
2382-
}
2383-
2384-
impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
2385-
where
2386-
F: FnMut(&'a Captures<'_>) -> O,
2387-
O: AsRef<str>,
2388-
{
2389-
type Output = O;
2374+
/// Contains helper trait for blanket implementation for [`Replacer`].
2375+
mod replacer_closure {
2376+
use super::*;
2377+
/// If a closure implements this for all `'a` and `'b`, then it also
2378+
/// implements [`Replacer`].
2379+
pub trait ReplacerClosure<'a>
2380+
where
2381+
Self: FnMut(&'a Captures<'_>) -> <Self as ReplacerClosure<'a>>::Output,
2382+
{
2383+
/// Return type of the closure (may depend on lifetime `'a`).
2384+
type Output: AsRef<str>;
2385+
}
2386+
impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
2387+
where
2388+
F: FnMut(&'a Captures<'_>) -> O,
2389+
O: AsRef<str>,
2390+
{
2391+
type Output = O;
2392+
}
23902393
}
2394+
use replacer_closure::*;
23912395

23922396
/// A trait for types that can be used to replace matches in a haystack.
23932397
///
@@ -2421,6 +2425,69 @@ where
24212425
/// let result = re.replace("Springsteen, Bruce", NameSwapper);
24222426
/// assert_eq!(result, "Bruce Springsteen");
24232427
/// ```
2428+
///
2429+
/// # Implementation by closures
2430+
///
2431+
/// Closures that take an argument of type `&'a Captures<'b>` for any `'a` and
2432+
/// `'b: 'a` and which return a type `T: AsRef<str>` (that may depend on `'a`)
2433+
/// implement the `Replacer` trait through a blanket implementation.
2434+
///
2435+
/// A simple example looks like this:
2436+
///
2437+
/// ```
2438+
/// use regex::{Captures, Regex};
2439+
///
2440+
/// let re = Regex::new(r"[0-9]+").unwrap();
2441+
/// let result = re.replace_all("1234,12345", |caps: &Captures<'_>| {
2442+
/// format!("[number with {} digits]", caps[0].len())
2443+
/// });
2444+
/// assert_eq!(result, "[number with 4 digits],[number with 5 digits]");
2445+
/// ```
2446+
///
2447+
/// Note that the return type of the closure may depend on the lifetime of the
2448+
/// reference that is passed as an argument to the closure. This requires the
2449+
/// closure to be a function, unless [closure lifetime binders] are being used:
2450+
///
2451+
/// [closure lifetime binders]: https://rust-lang.github.io/rfcs/3216-closure-lifetime-binder.html
2452+
/// [`Cow`]: std::borrow::Cow
2453+
///
2454+
/// ```
2455+
/// use regex::{Captures, Regex, Replacer};
2456+
/// use std::borrow::Cow;
2457+
///
2458+
/// let re = Regex::new(r"[0-9]+").unwrap();
2459+
/// fn prepend_odd<'a>(caps: &'a Captures<'_>) -> Cow<'a, str> {
2460+
/// if caps[0].len() % 2 == 1 {
2461+
/// Cow::Owned(format!("0{}", &caps[0]))
2462+
/// } else {
2463+
/// Cow::Borrowed(&caps[0])
2464+
/// }
2465+
/// }
2466+
/// let result = re.replace_all("1234,12345", prepend_odd);
2467+
/// assert_eq!(result, "1234,012345");
2468+
/// ```
2469+
///
2470+
/// The same example using closure lifetime binders:
2471+
///
2472+
/// ```
2473+
/// #![feature(closure_lifetime_binder)]
2474+
///
2475+
/// use regex::{Captures, Regex, Replacer};
2476+
/// use std::borrow::Cow;
2477+
///
2478+
/// let re = Regex::new(r"[0-9]+").unwrap();
2479+
/// let result = re.replace_all(
2480+
/// "1234,12345",
2481+
/// for<'a, 'b> |caps: &'a Captures<'b>| -> Cow<'a, str> {
2482+
/// if caps[0].len() % 2 == 1 {
2483+
/// Cow::Owned(format!("0{}", &caps[0]))
2484+
/// } else {
2485+
/// Cow::Borrowed(&caps[0])
2486+
/// }
2487+
/// },
2488+
/// );
2489+
/// assert_eq!(result, "1234,012345");
2490+
/// ```
24242491
pub trait Replacer {
24252492
/// Appends possibly empty data to `dst` to replace the current match.
24262493
///

0 commit comments

Comments
 (0)