I'm building a client for a REST SaaS service we'll call ExampleService
, so I have a client struct named ExampleClient
.
One component of this is that I've defined a trait for providing credentials to the client:
pub trait CredentialProvider {
fn provide_token(&self) -> impl Future<Output=Option<&str>>;
}
There are multiple implementations for getting credentials from disk, from an environment variable, from memory, etc. For now, here's an in-memory provider:
pub struct DirectCredentialProvider {
token: String,
}
impl CredentialProvider for DirectCredentialProvider {
async fn provide_token(&self) -> Option<&str> {
Some(&self.token.as_str())
}
}
Note that it's important for users to have the ability to define their own implementations if they want/need them, e.g. a Hashicorp Vault credential provider.
Finally, here's my client struct:
pub struct ExampleClient<P> {
credential_provider: P,
}
Unfortunately, as I add more functionality to the client, I fear that the generics are only going to increase as there will likely be similar features for providing different implementations of things, and so I'm trying to figure out how to "erase" the <P>
from the type definition.
The most obvious solution is to just box it:
pub struct ExampleClient {
credential_provider: Box<dyn CredentialProvider>,
}
I feel like I've seen examples of "erasing" the generic parameter in a client struct before, but I admit that I have no idea how it works under the hood.
Are there any other alternatives to just Box<dyn CredentialProvider>
? I want my users to be able to accept a &ExampleClient
and not have to deal with the generic parameter(s).