-
Notifications
You must be signed in to change notification settings - Fork 444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More generic impl of Replacer for closures #1048
Conversation
Implement `Replacer` also for closures that return a type which depends on lifetime of the argument. This allows performing a no-op replacement where the closure returns the whole match, for example, without needing to clone the captured string.
Fixed code formatting using rustfmt.
Less generic implementation, which results in a better documented API.
This is interesting. Can you explain how it works? Also, adding a doc test to confirm it fixes what you think it does would be helpful. (Although I'm unlikely to merge this, see below.) One part I'm concerned about here is I'm not too keen on this particular solution personally indeed because of the extra public trait here. It seems like a small thing, but it makes something that is already a little difficult to understand even more difficult. |
Oh, I see from the urlo post that this only helps cases that enable the |
Do not use the same lifetime for reference and type parameter of `Captures`.
I think this should not be an issue due to subtyping, but I fixed it anyway, see adf66a8. That last change requires a slightly different usage now:
I will try to do both later.
Targeting a potential new However, I agree that the helper trait is a downside and makes the API appear more complex.
In either case, it would probably be wise to wait until/if |
Gotya. And yeah I would be happy to re-litigate this in more detail if and when a regex 2.0 ever happens. |
Hide `ReplacerClosure` trait as an implementation detail and describe for which closures the `Replacer` trait is implemented in the documentation instead. Added documentation tests.
Fixed error in documentation comment in private module.
Ignore documentation test that needs unstable Rust.
That issue is addressed now by hiding the trait in a private module, see a40d8f5. The added documentation of
I think this should rather depend on the Note, however, that even using stable Rust, there is a use case. The relaxed bound can be used for functions (instead of closures) that return
|
Do not use closure lifetime binder but helper function for coercion in documentation test.
It's also possible to use the relaxed bound for closures on stable Rust as pointed out in this URLO post. I updated the documentation test, see 43222f2. |
Added documentation comment on `Replacer` impl for closures.
Basically the idea is to work around the problem that when specifying a bound for a closure, e.g. The idea of this PR is to use a helper trait which allows to add a bound that doesn't specifies the return type In the most recent draft, I have hidden the helper trait as an implementation detail in a private module. With 2c8c8dd, a documentation comment explains what the (hidden) complex implementation actually means. Hiding the helper trait should give some leverage when future language features are added to Rust, which might render the helper trait superfluous. |
Removed unnecessary import of `Replacer` in doc test.
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`.
I was undoing that change again in 664a0f2 because it is sufficient for a closure to accept This also allows a closure to return a type that depends either on |
With the recent changes, I would like to know if this has a chance to land (before I tried to address all concerns. Specifically:
If there is further interest in this, I could reproduce the changes also for the |
What if Beside, it'd be better to keep the lifetime contract right instead of weirdly working. |
That would already be a breaking change, so I don' think that's a problem.
I'm not sure what's "right" here. Consider the already existing Not sure what's the correct approach. I tried to use two different lifetimes, but then the helper trait I tried this: - pub trait ReplacerClosure<'a>
+ pub trait ReplacerClosure<'a, 'b>
where
- Self: FnMut(&'a Captures<'a>) -> <Self as ReplacerClosure<'a>>::Output,
+ 'b: 'a,
+ Self: FnMut(&'a Captures<'b>) -> <Self as ReplacerClosure<'a, 'b>>::Output,
{
- /// Return type of the closure (may depend on lifetime `'a`).
+ /// Return type of the closure (may depend on lifetime `'a` or `'b`).
type Output: AsRef<str>;
}
- impl<'a, F: ?Sized, O> ReplacerClosure<'a> for F
+ impl<'a, 'b, F: ?Sized, O> ReplacerClosure<'a, 'b> for F
where
- F: FnMut(&'a Captures<'a>) -> O,
+ 'b: 'a,
+ F: FnMut(&'a Captures<'b>) -> O,
O: AsRef<str>,
{ But then I also have to do that: -impl<F: for<'a> ReplacerClosure<'a>> Replacer for F {
+impl<F: for<'a, 'b: 'a> ReplacerClosure<'a, 'b>> Replacer for F { Which isn't valid syntax (I also asked on URLO about this):
If I instead use: impl<F: for<'a, 'b> ReplacerClosure<'a, 'b>> Replacer for F { then the compiler won't be able to tell that
I believed using a single lifetime instead of two lifetimes is okay here (which avoids these problems). But I'm not sure. Note that counterintuitively, using a single lifetime Demanding I think the current approach is harmless, though it's not possible to go from Also see: rust-lang/rfcs#3261. |
This is an alternative flavor of PR rust-lang#1048: Require `Replacer` closures to take a `&'a Captures<'b>` (for all `'a, 'b: 'a`) as argument and have a return type that must not depend on `'b` (but only on `'a`).
Thanks for the explanation, that was helpful. To say where I'm at currently:
|
@BurntSushi thanks for your feedback.
Nonetheless, having an unnameable trait showing up in the API can be considered somewhat ugly (similar to the sealed trait pattern that's commonly used).
I understand, and I don't fully overlook this either.
Would you reconsider this if/when I would propose to add these changes to the draft to make the current approach more "clean" and to highlight the open issues, and then close this PR for future reference. It could be reopened or recreated when there is some progress on that matter. |
Refactored code to be able to use two different lifetimes `'a` and `'b` for the `&'a Captures<'b>` argument while allowing the return type to depend on either `'a` or `'b`.
Do not mention unstable features in documentation. Do not include coercing helper function in example code.
Thanks to this post I have been able to refactor the code to use two different lifetimes See also: which is related to the problems I encountered when solving this.
I have improved that (1aab0b8). The example code isn't using any hacks anymore (but simply a straight-forward function). |
SGTM. I'm open to re-considering, but probably some effort will need to be put into ensuring this isn't a breaking change. (I don't necessarily mean "technically breaking" but rather "practically breaking.") The existence of the helper trait is a bummer but not a blocker for me. The breaking changes and the requirement to use |
Closing this then. Could be reconsidered when or a similar mechanism is implemented, which makes using such closures more idiomatic. Thanks for all the feedback and assessment. |
Implement
Replacer
also for closures that return a type which depends on lifetime of the argument. This allows performing a no-op replacement where the closure returns the whole match, for example, without needing to clone the captured string.See Conditional regex replacement on URLO for the genesis of this approach.