Skip to content

Commit 664a0f2

Browse files
committed
fixup! More generic impl of Replacer for closures
Use same lifetime for reference and type parameter of `Captures` (again) because `Captures<'a>` is covariant over `'a`. This covers closures that accept a `&'a Captures<'b>` as argument and have a result type that depends either on `'b` or `'a`. Documentation was updated and corresponding test cases have been added to `tests/misc.rs`. A link to the blanket implementation has been added to the "Implementation by closures" section of the documentation on `Replacer`.
1 parent 657dab8 commit 664a0f2

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

src/regex/string.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2378,14 +2378,14 @@ mod replacer_closure {
23782378
/// [`Replacer`].
23792379
pub trait ReplacerClosure<'a>
23802380
where
2381-
Self: FnMut(&'a Captures<'_>) -> <Self as ReplacerClosure<'a>>::Output,
2381+
Self: FnMut(&'a Captures<'a>) -> <Self as ReplacerClosure<'a>>::Output,
23822382
{
23832383
/// Return type of the closure (may depend on lifetime `'a`).
23842384
type Output: AsRef<str>;
23852385
}
23862386
impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
23872387
where
2388-
F: FnMut(&'a Captures<'_>) -> O,
2388+
F: FnMut(&'a Captures<'a>) -> O,
23892389
O: AsRef<str>,
23902390
{
23912391
type Output = O;
@@ -2429,8 +2429,10 @@ use replacer_closure::*;
24292429
/// # Implementation by closures
24302430
///
24312431
/// 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.
2432+
/// `'b: 'a` and which return a type `T: AsRef<str>` (that may depend on `'a`
2433+
/// or `'b`) implement the `Replacer` trait through a [blanket implementation].
2434+
///
2435+
/// [blanket implementation]: Self#impl-Replacer-for-F
24342436
///
24352437
/// A simple example looks like this:
24362438
///
@@ -2578,7 +2580,7 @@ impl<'a> Replacer for &'a Cow<'a, str> {
25782580
/// ```ignore
25792581
/// impl<F, T> Replacer for F
25802582
/// where
2581-
/// F: for<'a> FnMut(&a Captures<'_>) -> T,
2583+
/// F: for<'a> FnMut(&'a Captures<'a>) -> T,
25822584
/// T: AsRef<str>, // `T` may also depend on `'a`, which cannot be expressed easily
25832585
/// {
25842586
/// /* … */

tests/misc.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,49 @@ fn dfa_handles_pathological_case() {
141141
};
142142
assert!(re.is_match(&text));
143143
}
144+
145+
// Test if implementation of `Replacer` for closures covers any reasonable
146+
// lifetime combination in regard to the argument and return type.
147+
mod replacer_closure_lifetimes {
148+
use regex::{Captures, Regex};
149+
use std::borrow::Cow;
150+
#[test]
151+
fn reference_lifetime() {
152+
fn coerce<F: for<'a> FnMut(&'a Captures<'_>) -> Cow<'a, str>>(
153+
f: F,
154+
) -> F {
155+
f
156+
}
157+
let s = Regex::new("x")
158+
.unwrap()
159+
.replace_all("x", coerce(|caps| Cow::Borrowed(&caps[0])));
160+
assert_eq!(s, "x");
161+
}
162+
#[test]
163+
fn parameter_lifetime() {
164+
fn coerce<F: for<'b> FnMut(&Captures<'b>) -> Cow<'b, str>>(f: F) -> F {
165+
f
166+
}
167+
let s = Regex::new("x").unwrap().replace_all(
168+
"x",
169+
coerce(|caps| Cow::Borrowed(caps.get(0).unwrap().as_str())),
170+
);
171+
assert_eq!(s, "x");
172+
}
173+
// Additionally demand that its sufficient if the closure accepts a single
174+
// lifetime `'u` which is used both for the reference to and the lifetime
175+
// argument of the `Captures` argument. Note that `Captures<'u>` is
176+
// covariant over `'u`.
177+
#[test]
178+
fn unified_lifetime() {
179+
fn coerce<F: for<'u> FnMut(&'u Captures<'u>) -> Cow<'u, str>>(
180+
f: F,
181+
) -> F {
182+
f
183+
}
184+
let s = Regex::new("x")
185+
.unwrap()
186+
.replace_all("x", coerce(|caps| Cow::Borrowed(&caps[0])));
187+
assert_eq!(s, "x");
188+
}
189+
}

0 commit comments

Comments
 (0)