Open
Conversation
These traits are supposed to provide abstractions over standard references and protobuf views and muts to eliminate hard dependency on protobuf The biggest challenge in the message module is to solve the long-standing difficulty of reborrowing mutable message views. The Problem: Reborrowing GATs When working with Generic Associated Types (GATs) for mutable views (e.g., type Mut<'a>), we frequently need to "reborrow" a view to pass it to a child function without consuming the original. However, traditional approaches fail here: 1. Foreign Types: We cannot add a .reborrow() method directly to protobuf::Mut as it is a foreign type. 2. HRTB Hell: Defining a separate Reborrow trait and requiring for<'a> T::Mut<'a>: Reborrow leads to complex Higher-Ranked Trait Bound errors (e.g., "implementation is not general enough") that are extremely difficult to satisfy in Rust's current type system. The Solution: Static Operator Pattern We bypass these issues by moving the reborrowing logic into the provider trait itself as a static operator. * Introduced AsMut::reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>. * This bundles the reborrowing logic with the type definition, avoiding the need for extra traits or complex HRTB bounds on the view type itself. Changes: * as_mut.rs: Defined AsMut with the reborrow_view static operator. Implemented it for protobuf::Message and added tests verifying that views can be reborrowed and mutated without consuming the original. * as_view.rs: Defined AsView for immutable views.
This introduces a push-based stream API (`PushStream`) for gRPC server responses, modeled after internal C++ patterns to address limitations in the current pull-based model. Key architectural decisions: * **Push vs. Pull:** Standard streams yield `Result<Message, Error>`, but gRPC strictly requires messages followed by a terminal error. A unified async execution context also prevents partial consumption bugs and eliminates the performance overhead of constantly wrapping/unwrapping items in a `Result`. * **Generics over Dynamic Dispatch:** Generics are used instead of `dyn` APIs (like `RecvStream`) to enable nightly specialization. This provides power users with a workaround for Rust's limited native RTTI without requiring custom vtables. * **Struct Wrappers over Raw Traits:** The underlying traits are encapsulated in struct wrappers to future-proof the API. This gives the gRPC layer the flexibility to transparently manage features like task locals later without breaking public stability. Known Pitfalls: * **Stateless Consumer API:** The consumer API is completely stateless and is never explicitly made aware of the end of the stream. Consequently, there is currently no zero-cost or trivial way to implement asynchronous cleanups when the stream concludes.
Introduce the public API traits for server-side gRPC methods (`UnaryMethod`, `ClientStreamingMethod`, `ServerStreamingMethod`, `BidiStreamingMethod`). These traits are designed to be the primary interface for codegen. Currently, the API models a handler as an async function with a (requeststream, responsesink) interface. Note on design: - The public API is scoped to access request and response messages only. - Modifications to headers and trailers are intended to be handled via "interceptor" APIs, following the Java gRPC model. - Contextual information is expected to be accessed via a read-only local context. Known pitfalls: - Unary inputs are references, implying that they will not trivially work with spawns. While responses can be shallow copied due to them being mut refs, requests will have to be copied if they need to be sent to a different task.
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.
Add Public handler API traits and underlying core underlying public structures and APIs
Summary
This pull request introduces the foundational API traits, Push Stream model, and Protobuf view abstractions to establish the primary interfaces for the new generic gRPC server architecture. It eliminates hard Protobuf dependencies, resolves GAT reborrowing constraints, and introduces a more performant streaming execution model.
Key Architectural Changes
1. Push-Based Stream API (
PushStream)Replaces the standard pull-based streaming model with a C++ inspired, push-based (
PushStream) API for server responses. This is the most significant structural change, designed to resolve limitations in the current model:dynAPIs (e.g.,RecvStream) with generics. This enables nightly specialization, offering power users a performant workaround for Rust's limited native RTTI without relying on custom vtables.Result<Message, Error>, directly streamlining the message delivery pipeline.2. Server-Side Method Traits for Codegen
Introduces
UnaryMethod,ClientStreamingMethod,ServerStreamingMethod, andBidiStreamingMethodas the definitive interfaces for code generation.(request_stream, response_sink)signature.3. Message View Abstractions (
AsView&AsMut)Decouples the core implementation from the
protobufcrate by abstracting standard references and Protobuf views.AsMut::reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>) directly bound to the provider trait, successfully allowing mutable view reborrowing without consuming the original reference.