Summary
Allow the use of concrete types in where clause, regardless of whether they implement the traits or not.
Motivation
Regularity
As of today, it is possible to use concrete types in a where clause, but only if the predicate is satisfied.
That is, the following code compiles:
#[derive(Default)]
pub struct Foo(i64);
pub fn foo() -> Foo
where
Foo: Default,
{
Foo::default()
}
While commenting out the #[derive(Default)] leads to a compilation error:
error[E0277]: the trait bound `Foo: Default` is not satisfied
--> src/lib.rs:6:5
|
6 | Foo: Default,
| ^^^^^^^^^^^^ the trait `Default` is not implemented for `Foo`
|
= help: see issue #48214
help: consider annotating `Foo` with `#[derive(Default)]`
|
2 + #[derive(Default)]
3 | pub struct Foo(i64);
|
Feature-less feature-detection
While exploring the solutions to rusts #2396, one elegant solution which popped in my mind was:
- Have a feature in the rustls crate, upon which a
CryptoProviderwould implementDefault. - Condition the presence of certain APIs on
CryptoProviderimplementingDefault.
Now, within the crate, said APIs can easily be conditioned on the presence of the feature, obviously. The problem is that outside the crate, this feature cannot be checked, and therefore:
- Each dependent crate needs its own matching feature.
- Each user of those dependent crates need to activate all those matching features (if they use several independent crates).
This is a pain for both library writers and application writers.
Guide-level explanation
A where clause may be used to verify pre-conditions on any type:
- Concrete types, such as
String. - Concrete types with generic parameters, such as
Vec<T, A>. - Generic parameters, such as
T.
For example:
#[cfg_attr(feature = "default-crypto-provider", derive(Default))]
struct CryptoProvider { /**/ }
impl ClientConfig {
/// Creates a builder for the ClientConfig.
pub fn builder() -> ConfigBuilder<Self, WantsVersions>
where
CryptoProvider: Default
{
Self::builder_with_provider(CryptoProvider::default())
}
}
Reference-level explanation
TBD
Drawbacks
- Unclear how to difficult to implement it is.
Rationale and alternatives
Rationale
- Regularity is good, it removes unexpected papercuts from the language.
- The feature is genuinely useful, as motivated above.
Alternatives
- Do nothing: let library authors and users get themselves tangled up in crate features. They love it, I swear!
- Dependency Feature Testing: allow testing library features, ie
#[cfg(feature = "rustls::default-crypto-provider")], which is independently useful. It's clunky, but at least it allows downstream libraries not to have to create their own features for it, and it means users don't have to enable the same feature 40 times in their Cargo.toml.
Prior art
I could not find any for this specific feature. The usefulness of where clause probably need no motivation of its own.
Unresolved questions
None, right now.
Future possibilities
Unclear whether there are more "non-allowed" pieces on the left hand of where clauses which could warrant looking into.