It should work, no? I’m imagining fn(impl Into<Cow<'a, str>>) -> Cow<'a, str> with matching lifetimes. It’s of course also a bit non-trivial to decide how the owned case should be handled in the first place. I imagine, the String could possibly, maybe, potentially… or maybe not… be re-used for the output String even if some replacing happens… though certainly at least in the no-changes-made case it could be retained as-is. (Such things would have to be documented ^^.)
A fn(impl Into<Cow<'a, str>>) -> Cow<'a, str> (or equivalently, apart from the convenience, a fn(Cow<'a, str>) -> Cow<'a, str>) function is a bit like two functions: one fn(&'a str) -> Cow<'a, str> function plus one fn(String) -> Cow<'a, str> function (though the latter has no input to borrow from, so commonly it would be like a fn(String) -> String function, except maybe when &'static str return values are an option for certain cases). Offering two such functions distinctly would allow callers to wrap them up into a single fn(Cow<'a, str>) -> Cow<'a, str> function themselves; and if the only optimized case of the fn(String) -> String one is to keep the String untouched when no changes are being made, then by inspecting the result of the fn(String) -> Cow<'a, str> function, a user can write their own fn(Cow<'a, str>) -> Cow<'a, str> wrapper easily.
In fact, it looks like Regex::replace only ever returns exactly the original &str in the Cow::Borrowed case, it might just as well be a fn(&str) -> Result<String, NoMatchesFound> kind of function, leaving keeping around the &str to the caller. Of course, the benefit of not doing that can be increased convenience for the user out of the box. But the more complex the type signature, the more possible smart things or optimizations (such as the above-mentioned idea that maybe replacing within a String could happen in-place, using existing String capacity if available) that users might (incorrectly) expect need to be rules out in the documentation.
This whole discussion also makes me wonder about – something I have never tried to think too deeply about before yet – what the “ideal” API for HashMap would look like, where at the moment HashMap::entry has the unfortunate API design of always requiring an owned key. And sometimes, say for a HashMap<String, T>, you already have an owned String you no longer need afterwards, anyways; but if you only have a &str, unconditionally cloning the thing seems “wasteful”. I don’t know how closely or loosely related such an entry API would be to the things discussed here, but it’s not entirely different, I believe.