Skip to content

feat(layered): relax overly restrictive static bounds#373

Open
martintmk wants to merge 9 commits intomainfrom
overly-restrictive-static-bounds
Open

feat(layered): relax overly restrictive static bounds#373
martintmk wants to merge 9 commits intomainfrom
overly-restrictive-static-bounds

Conversation

@martintmk
Copy link
Copy Markdown
Member

@martintmk martintmk commented Apr 14, 2026

There are few places where the 'static requirement is unnecessarily enforced.

Copilot AI review requested due to automatic review settings April 14, 2026 06:14
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (5593a3e) to head (509f52f).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@martintmk martintmk force-pushed the overly-restrictive-static-bounds branch from a09c688 to 3f364c7 Compare April 14, 2026 06:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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::new bounds by removing 'static requirements from In, Out, and the returned Future.
  • Add impl From<std::io::ErrorKind> for recoverable::RecoveryInfo (with tests) to provide a shared IO retry classification.
  • Remove http_extensions’ internal resilience module and switch HttpError’s IO recovery to the new RecoveryInfo::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 ErrorKindRecoveryInfo 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.

Comment thread crates/layered/src/execute.rs
Copilot AI review requested due to automatic review settings April 14, 2026 07:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/seatbelt/src/fallback/callbacks.rs
Copy link
Copy Markdown
Member

@sandersaares sandersaares left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding some tests with non-'static data. I see that some bounds were removed but I see no evidence it actually works.

@sandersaares
Copy link
Copy Markdown
Member

I see a bunch of tests added now but aren't those all still 'static? Am I missing the non-'static references somewhere?

Copilot AI review requested due to automatic review settings April 14, 2026 17:49
@martintmk
Copy link
Copy Markdown
Member Author

I see a bunch of tests added now but aren't those all still 'static? Am I missing the non-'static references somewhere?

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;

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/seatbelt/tests/timeout.rs Outdated
Comment thread crates/layered/tests/execute.rs Outdated
Comment thread crates/seatbelt/tests/references.rs Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 14, 2026 19:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread crates/layered/tests/intercept.rs Outdated
@sandersaares
Copy link
Copy Markdown
Member

The explicit type annotation makes it clear that we are dealing with non-static strings:

I do not see any non-'static lifetimes listed in the code you pasted, though. Am I missing something?

And just to make sure, I did update the "references.cs" composite test:

OK, that pattern does look believable, though.

@martintmk martintmk enabled auto-merge (squash) April 15, 2026 06:03
Copilot AI review requested due to automatic review settings April 15, 2026 06:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-'static In/Out, but DynamicService still hard-codes DynService<'static, In, Out>. If the lifetime parameter on DynService represents the returned future’s lifetime (a common pattern for boxed futures), this effectively reintroduces a 'static requirement and can make the API misleading (bounds say it’s allowed, representation still forces 'static). Consider either (a) restoring In: 'static, Out: 'static bounds on DynamicService/DynamicServiceExt to match the representation, or (b) changing DynamicService to store a higher-ranked or non-'static dyn 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.

Comment thread crates/seatbelt/tests/timeout.rs
Copy link
Copy Markdown

@afoxman afoxman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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 :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am starting to realize that this indeed might not be possible with current Rust :(

@martintmk
Copy link
Copy Markdown
Member Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants