A ton of t-lang design decisions hinge on thinking about "how can this code evolve", "what am I promising my users by writing this code", etc. And a lot of feature bikeshedding is about choosing sane defaults so crate authors know what they're committing to. I'm thinking we could make this a more explicit part of the language by letting crate authors more explicitly opt-in/opt-out to future changes.
My idea was a future_proof
attribute that constrains what is or isn't allowed as a non-semver-breaking change:
- An enum with
#[future_proof(allow(add_variants))]
is the same as a#[non_exhaustive]
enum. - An enum with
#[future_proof(forbid(add_variants))]
is a normal enum. - An async method could be
#[future_proof(implements(Send))]
. - A struct could be
#[future_proof(covariant('a))]
. - A trait with
#[future_proof(forbid(add_impl))]
is effectively sealed. - A trait could be
#[future_proof(dyn_safe)]
. - Some appropriate
future_proof
on a trait could relax or strengthen coherence rules. - etc
Idk if all these examples make sense, but you get the gist: crate authors have to think about semver-compatibility, but it's not always obvious what they're committing to or how to commit to something else. We usually provide future-compatibility knobs with various attributes or impls or things. Having a single attribute would make all these commitments discoverable in a single place. We could recommend using this attribute in api-guidelines, and provide typical choices for typical kinds of crates.
Does that make sense? Any other killer examples? Has something like this been proposed before? Even without the attribute, I think there's something to be said about making future-commitments a more first-class citizen in the language.