Indeed, we could make our own trait with blanket implementation (aka trait alias) to work around this (just hypothetically exploring this idea):
pub trait GenericFnOnce1Arg<Arg>
where
Self: FnOnce(Arg) -> <Self as GenericFnOnce1Arg<Arg>>::Output
{
type Output;
}
impl<T: ?Sized, Arg, Ret> GenericFnOnce1Arg<Arg> for T
where T: FnOnce(Arg) -> Ret,
{
type Output = Ret;
}
Now we can use a bound like that:
fn use_it<F>(_val: F)
where
F: for<'a> GenericFnOnce1Arg<&'a Outer>,
{}
Here, the return type does not need to be named (but could have additional bounds added, like AsRef<str>
, if desired [1]).
Now, using the nightly #![feature(closure_lifetime_binder)
, we can use this API:
fn f(o: &Outer) -> &Inner {
&o.field
}
fn main() {
use_it(for<'a> |outer: &'a Outer| -> &'a Inner { &outer.field });
use_it(f);
}
Update: Combining all these ideas, and getting back to the original problem, we could do:
#![feature(closure_lifetime_binder)]
struct MyReplacer<F>(F);
impl<F> Replacer for MyReplacer<F>
where
F: for<'a> GenericFnMut1Arg<&'a Captures<'a>>,
for<'a> <F as GenericFnMut1Arg<&'a Captures<'a>>>::Output: AsRef<str>,
{
fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {
dst.push_str((self.0)(caps).as_ref())
}
}
fn main() {
let s1 = "Hello World!";
let s2 = Regex::new("World").unwrap().replace_all(s1, "Universe");
assert_eq!(s2, "Hello Universe!");
let s3 = Regex::new("World").unwrap().replace_all(
s1,
MyReplacer(
for<'a> |caps: &'a Captures<'a>| -> &'a str {
let universal: bool = false; // actually some more complex computation
if universal {
"Universe"
} else {
&caps[0] // don't replace
}
}
)
);
assert_eq!(s3, "Hello World!");
}
I don't want to say it's a good or idiomatic way to go. But it's curious that it's possible to solve this issue in that way (on Nightly, and being willing to use a newtype wrapper to adjust regex
's API)!
(Playground) ↩︎