feat(layered): relax overly restrictive static bounds#373
feat(layered): relax overly restrictive static bounds#373
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #373 +/- ##
=======================================
Coverage 100.0% 100.0%
=======================================
Files 223 223
Lines 16065 16065
=======================================
Hits 16065 16065 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
a09c688 to
3f364c7
Compare
There was a problem hiding this comment.
Pull request overview
This PR relaxes some generic bounds on layered::Execute::new and centralizes IO error recoverability classification into the recoverable crate, replacing an internal helper previously living in http_extensions.
Changes:
- Relax
Execute::newbounds by removing'staticrequirements fromIn,Out, and the returnedFuture. - Add
impl From<std::io::ErrorKind> for recoverable::RecoveryInfo(with tests) to provide a shared IO retry classification. - Remove
http_extensions’ internalresiliencemodule and switchHttpError’s IO recovery to the newRecoveryInfo::from(error.kind())path.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/recoverable/src/lib.rs | Adds the new io module to compile in IO-to-RecoveryInfo conversions. |
| crates/recoverable/src/io.rs | Introduces IO ErrorKind → RecoveryInfo classification and corresponding unit tests. |
| crates/layered/src/execute.rs | Relaxes overly restrictive 'static bounds for Execute::new generics. |
| crates/http_extensions/src/resilience.rs | Removes the now-duplicated IO recoverability helper. |
| crates/http_extensions/src/lib.rs | Drops the internal resilience module from the crate module list. |
| crates/http_extensions/src/error.rs | Updates IO error recovery classification to use RecoveryInfo::from(error.kind()). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I see a bunch of tests added now but aren't those all still |
The explicit type annotation makes it clear that we are dealing with non-static strings: let context: ResilienceContext<&str, &str> = ResilienceContext::new(&clock);And just to make sure, I did update the "references.cs" composite test: let input = "hello".to_string();
let service = stack.into_service();
let output = service.execute(&input).await; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
I do not see any non-
OK, that pattern does look believable, though. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
crates/layered/src/dynamic.rs:60
- The impl bounds were relaxed to allow non-
'staticIn/Out, butDynamicServicestill hard-codesDynService<'static, In, Out>. If the lifetime parameter onDynServicerepresents the returned future’s lifetime (a common pattern for boxed futures), this effectively reintroduces a'staticrequirement and can make the API misleading (bounds say it’s allowed, representation still forces'static). Consider either (a) restoringIn: 'static, Out: 'staticbounds onDynamicService/DynamicServiceExtto match the representation, or (b) changingDynamicServiceto store a higher-ranked or non-'staticdyn service (e.g.,for<'a> DynService<'a, In, Out>) so the relaxed bounds are actually realizable.
pub struct DynamicService<In, Out>(Arc<DynService<'static, In, Out>>);
impl<In: Send, Out: Send> DynamicService<In, Out> {
pub(crate) fn new<T>(strategy: T) -> Self
where
T: Service<In, Out = Out> + Send + Sync + 'static,
{
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
afoxman
left a comment
There was a problem hiding this comment.
Thanks for refining the lifetimes all over the crate!
| } | ||
|
|
||
| struct OnInput<In>(Arc<dyn Fn(&In) + Send + Sync>); | ||
| struct OnInput<In>(Arc<dyn for<'a> Fn(&'a In) + Send + Sync>); |
There was a problem hiding this comment.
| struct OnInput<In>(Arc<dyn for<'a> Fn(&'a In) + Send + Sync>); | |
| struct OnInput<'a, In>(Arc<dyn Fn(&'a In) + Send + Sync>); |
I think something like this will get you closer, but the hang-up is that the closure lifetime is much longer than &In... and the closure could borrow and hold using the ref.
A stateless function, on the other hand, can't do that. So it solves the problem, but takes away closures. And users who need stateful calls would have to put their "context" data in In which is gross. Sorry, all I have is bad news :|
A cheaper, backward compatible fix would be to document that this isn't supported :)
There was a problem hiding this comment.
I am starting to realize that this indeed might not be possible with current Rust :(
|
currently, we haven't found a way to make this work with references without poisoning the service lifetime. The go-to solution is to use owned types for now. |
There are few places where the 'static requirement is unnecessarily enforced.