Skip to content

Implement ChannelCredentials for DynChannelCredentials#2674

Closed
nathanielford wants to merge 4 commits into
grpc:masterfrom
nathanielford:refactor/into-dyn-channel-creds
Closed

Implement ChannelCredentials for DynChannelCredentials#2674
nathanielford wants to merge 4 commits into
grpc:masterfrom
nathanielford:refactor/into-dyn-channel-creds

Conversation

@nathanielford

Copy link
Copy Markdown
Contributor

To support creating channels with various credentials configurations, we need to accept different credential representations (such as concrete types or Arc-wrapped trait objects). This trait provides a common conversion interface to type-erase these representations internally. This refactoring is isolated to its own commit to keep subsequent feature commits focused and legible.

Motivation

When working on the Channel builder, it became clear having into traits for DynChannelCredentials would be useful. Because this trait is private, being able to automatically convert public implementors of the ChannelCredentials trait to what we are using internally was the ergonomic option.

Solution

Add the into traits, update the relevant public traits to support it.

…tials.

To support creating channels with various credentials configurations, we need to accept different credential representations (such as concrete types or Arc-wrapped trait objects). This trait provides a common conversion interface to type-erase these representations internally. This refactoring is isolated to its own commit to keep subsequent feature commits focused and legible.
@nathanielford nathanielford requested a review from arjan-bal June 4, 2026 22:06
@nathanielford nathanielford marked this pull request as ready for review June 4, 2026 22:28
@nathanielford nathanielford enabled auto-merge (squash) June 4, 2026 22:28

@dfawley dfawley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure I understand exactly why this change is needed. I think we want to convert into the Dyn version of the trait internally, and not expose that trait to anyone else.

We already have the blanket impl of DynChannelCredentials -- why isn't that good enough? Is it because the blanket impl specifies T::Output more narrowly than the channel builder does?

@nathanielford

Copy link
Copy Markdown
Contributor Author

@arjan-bal I'm looking into other ways of achieving this, per Doug's comment about this maybe not being necessary, but you can look at what I'm attempting with the channel type builder here: master...nathanielford:grpc-rust-nford:implement/channel-builder

As soon as that's stable I'll create it's own PR for it, which currently relies on this PR, but if we decide we don't need it I can excise this chunk easily.

@arjan-bal

Copy link
Copy Markdown
Contributor

@nathanielford, I was looking at ChannelBuilder::credentials in #2675, but I'm having trouble understanding the need for the new IntoDynChannelCredentials trait. Could you clarify what issue prevents us from using the same input parameter as the current channel constructor (i.e., Arc<C> where C: ChannelCredentials, C::Output<Box<dyn GrpcEndpoint>>: GrpcEndpoint + 'static)?


For context, the constructor accepts an Arc<C> because the credentials passed during channel creation might be reused to create "out of band" (OOB) channels (e.g., to an RLS server). If we don't require the Arc in the input type and instead wrap the credentials inside the constructor, creating an OOB channel would result in wrapping them in an Arc a second time. This would lead to redundant pointer indirection and reference counting.

An alternative approach I considered was defining a wrapper struct:

pub struct SharedChannelCreds {
    inner: Arc<dyn DynChannelCredentials>,
}

The channel constructor would then accept impl Into<SharedChannelCreds>. We would provide a blanket impl<C: ChannelCredentials> From<C> for SharedChannelCreds and also implement ChannelCredentials for the wrapper itself.

This prevents double Arcing as a SharedChannelCreds object could be passed directly to the OOB channel, and calling .into() on it would simply return the object itself. However, we decided against this approach because it obscures the underlying API. Once the credentials APIs are unsealed, hiding the requirements behind Into<SharedChannelCreds> would make it harder for users to discover exactly which trait they need to implement for custom credentials.

@nathanielford

Copy link
Copy Markdown
Contributor Author

So, after trying to work this a couple of ways, I agree the Into... formulation I was trying was the wrong tree to bark up. However, I /am/ concerned about just taking an Arc<C> and would love your thoughts.

If the signature is this:

impl<Runtime> ChannelBuilder<MissingOpt, Runtime> {
    pub fn credentials<C>(self, credentials: Arc<C>) -> ChannelBuilder<PresentCredentials, Runtime>
    where
        C: crate::credentials::ChannelCredentials + 'static,
        C::Output<Box<dyn crate::rt::GrpcEndpoint>>: crate::rt::GrpcEndpoint,

That means that the user has to pass an Arc<C> of the credentials in, but it prevents them from passing a Arc<dyn DynChannelCredentials>. I /think/ this is needed because of load balancers that might need to make side subchannels to connect to things like liveness endpoints, because the credentials will have already been type-erased. (I might be wrong on this point? But trying to account for this (and avoid the double-indirection issue) was what caused the diversion here.)

Alright, so assuming I'm not wrong there, it seems like this can be worked out with an impl ChannelCredentials for dyn DynChannelCredentials. I think this resolves all the ergonomics/use case issues, but there may be a reason we didn't already do this.

For the sake of discussion I'll update this PR with the proposed impl.

…nd Arc<T> to eliminate custom conversion boilerplate.
@nathanielford nathanielford changed the title Introduce IntoDynChannelCredentials trait for flexible channel creds. Implement ChannelCredentials for DynChannelCredentials Jun 11, 2026
@nathanielford nathanielford force-pushed the refactor/into-dyn-channel-creds branch from 6029df0 to ee091b3 Compare June 11, 2026 18:42
@nathanielford

Copy link
Copy Markdown
Contributor Author

I'm closing this since I don't think it's immediately necessary. It may be that the concern about internal channels remains but the Channel API isn't dependent on it if we accept that the user is passing an Arc<C>. Thus, we can do it later when it's required.

auto-merge was automatically disabled June 11, 2026 20:00

Pull request was closed

@arjan-bal

Copy link
Copy Markdown
Contributor

I tried passing an Arc<dyn DynChannelCredentials> to Channel::new, but it fails to compile, even after adding an impl ChannelCredentials for dyn DynChannelCredentials.

error[E0277]: the size for values of type `dyn dyn_wrapper::DynChannelCredentials` cannot be known at compilation time
   --> grpc/src/credentials/dyn_wrapper.rs:307:38
    |
307 |         let ch = Channel::new("abc", creds, ChannelOptions::default());
    |                  ------------        ^^^^^ doesn't have a size known at compile-time
    |                  |
    |                  required by a bound introduced by this call
    |
    = help: the trait `Sized` is not implemented for `dyn dyn_wrapper::DynChannelCredentials`
note: required by an implicit `Sized` bound in `channel::Channel::new`
   --> grpc/src/client/channel.rs:170:16
    |
170 |     pub fn new<C>(target: impl Into<String>, credentials: Arc<C>, options: ChannelOptions) -> Self
    |                ^ required by the implicit `Sized` requirement on this type parameter in `Channel::new`
help: consider relaxing the implicit `Sized` restriction
   --> grpc/src/client/channel.rs:172:30
    |
172 |         C: ChannelCredentials + ?Sized,
    |                               ++++++++

For more information about this error, try `rustc --explain E0277`.

This happens because the generic type C passed to Channel::new must have a known size at compile time, which dyn DynChannelCredentials does not satisfy. The compiler strictly enforces this Sized bound because it requires it to perform the trait object coercion credentials as Arc<dyn DynChannelCredentials>.

I didn't catch this issue earlier. For now, I suggest sticking to the existing bounds on the Channel constructor. I'll think about this a bit more and share some ideas soon.

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.

3 participants