RFC 3678: Why should final rule out downstream use cases that library authors never anticipated?
I'd like to raise a concern about RFC 3678 β Final Trait Methods.
My concern is simple:
Why should a library author's inability to anticipate a future use case become a hard compiler restriction for everyone else?
This is an everyday reality of open-source development.
Many libraries start as small projects built for their authors' own needs. Some eventually become widely adopted, while others remain small but still have users with requirements the authors never considered.
Library authors cannot predict every future use case. Nor should we expect them to.
A method that seems to have no reason to be overridden today may have a perfectly legitimate reason to be overridden tomorrow.
Yet final turns the author's current assumption into an absolute restriction.
When an unforeseen requirement appears, the downstream user cannot simply override the method, even if they fully understand the consequences and are willing to take responsibility for their implementation.
Instead, they may have to fork the dependency, maintain patches, and deal with potential compatibility issues across updates. In complex workspaces, this can be an enormous amount of work just to change one method.
Why should we force downstream users to pay such a high price for requirements that the original author simply couldn't foresee?
I understand that some methods must not be overridden because soundness or other essential guarantees depend on their exact implementation. I'm not arguing against enforcing those guarantees.
But not every reason for marking a method final is a soundness requirement.
For cases where no hard guarantee depends on prohibiting overrides, I think a warning-based approach would be more appropriate.
My proposal: Warn by default, allow explicit overrides with a reason
Instead of always rejecting an override, the compiler could emit a warning:
impl SomeTrait for MyType {
fn method(&self) {
// Custom implementation
}
}
If the user intentionally overrides the method, they could explicitly acknowledge the warning and explain why:
impl SomeTrait for MyType {
#[expect(
final_method_override,
reason = "Required for compatibility with a legacy protocol"
)]
fn method(&self) {
// Custom implementation
}
}
Here, final_method_override is a hypothetical lint. Ideally, suppressing this warning would require an explicit reason.
This would mean:
- The library author's intention is still clearly communicated.
- Accidental overrides produce compiler warnings.
- Intentional overrides require an explicit, reviewable acknowledgement.
- Downstream users retain the ability to handle requirements the library author never anticipated.
For cases where overriding would violate soundness or other necessary guarantees, a separate strict mechanism could still prohibit it.
This approach feels much closer to the Rust philosophy I appreciate: make risks and responsibilities explicit, without unnecessarily removing the developer's ability to make informed decisions.
The question
I understand that absolute prohibition is a familiar and straightforward way to prevent unwanted overrides.
But being straightforward doesn't necessarily make it the right solution for every situation.
I worry that we are choosing an absolute restriction without giving enough weight to the long-term consequences for downstream extensibility.
A library author can know what their implementation needs today. They cannot know everything their users will need tomorrow.
Why should the language enforce the former at the expense of the latter, when a warning and explicit acknowledgement could offer a more flexible alternative?
I would really like to see this trade-off discussed before final is stabilized.