fix(util): make option_layer unify branch errors to BoxError#876
Open
ameyypawar wants to merge 5 commits into
Open
fix(util): make option_layer unify branch errors to BoxError#876ameyypawar wants to merge 5 commits into
ameyypawar wants to merge 5 commits into
Conversation
Introduce `OptionLayer`, the layer produced by `option_layer`. Unlike using `Either` directly, its service unifies the layered and unlayered branches' error types to `BoxError`, so an optional layer that changes the error type (e.g. `TimeoutLayer`) no longer breaks the resulting `Service` (tower-rs#665). This commit only adds the new module; `option_layer` is rewired to it in a follow-up commit.
…rror) `option_layer` returned `Either<L, Identity>`, whose `Service` impl requires both branches to share an error type. When the optional layer changes the error type (e.g. `TimeoutLayer`), the resulting service did not implement `Service` at all (tower-rs#665). Return the new `OptionLayer` instead, which unifies the layered and unlayered branches' error types to `BoxError`. Re-export `OptionLayer`/`OptionService` and the response future from `util`.
Reflect `option_layer` now returning `OptionLayer<T>` instead of `Either<T, Identity>`.
…-rs#665) Build `option_layer` over a layer that changes the error type and assert the result is a usable `Service`. Fails to compile before the fix (the two `Either` branches had different error types); passes now that they are unified to `BoxError`.
`option_layer` no longer constructs `Identity`, so the import is unused. Also bring `Service` into scope in the regression test so `.call()` resolves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #665.
option_layerreturnsEither<L, Identity>. The service-levelEither<A, B>requires both branches to share an error type (B: Service<Request, Response = A::Response, Error = A::Error>). So when the optional layer changes the error type — e.g.TimeoutLayer, whose service errors areBoxError— over a service with a different error type, the result does not implementServiceat all:The existing doc test didn't catch this because it never asserted that the result is a
Service.Why the fix has to box at the service level
A generic
Layer<S>impl can't nameS's error type —Layerhas noRequestparameter, so<S as Service<Request>>::Errorisn't in scope when the layer is applied. The error types are only known once a request type is fixed, i.e. in the produced service's ownServiceimpl. So the unification can't be done by composingMapErrLayeratoption_layertime; it must live in a dedicated service.The fix
Add
OptionLayer<L>(the layer now returned byoption_layer). Its producedOptionService<A, B>is likeEither, but itsServiceimpl setsError = BoxErrorwith boundsA::Error: Into<BoxError>,B::Error: Into<BoxError>, andB::Response = A::Response— so the layered and unlayered branches need not share an error type.ServiceBuilder::option_layer's return type is updated to match.This is semver-breaking and adds public API, so I'm raising it as a proposal rather than assuming the shape:
util::option_layer(Either<L, Identity>→OptionLayer<L>) andServiceBuilder::option_layer(Stack<Either<T, Identity>, L>→Stack<OptionLayer<T>, L>).util::OptionLayer,util::OptionService, andutil::future::OptionResponseFuture.OptionService'sSome/Nonevariants, theOptionResponseFuturealias, etc.).Eitherrework brokeoption_layer#665 wasimpl Layer<S> for Option<L>; that has the same error-unification need, so theOptionServiceboxing machinery would still be required underneath. Happy to reshape toward whichever direction you prefer.Tests / verification
tests/util/option_layer.rs) that buildsoption_layerover a layer which changes the error type and asserts the result is a usableService. It fails to compile before this change (the twoEitherbranches have different error types) and passes after.cargo fmt --check,cargo check --workspace --all-features --all-targets, the new test, and theoption_layer/ServiceBuilder::option_layerdoc tests all pass; clippy and the all-features build are warning-clean. The change uses onlymatch/Poll::map_err/Into::into, so it's within the current MSRV.