Description
RFC #255 glossed over the issue of static methods. With the current implementation (plus rust-lang/rust#18527) we ignore static methods when deciding on object-safety (pre rust-lang/rust#18527, we ICE'ed). However, this does not allow us to use object-safety to guide the use of trait objects with traits (the 'auto-impl's for trait objects). E.g.,
trait T {
fn foo();
}
fn bar<X: T>(x: Box<T>) {
X::foo();
}
fn main() {
let y: Box<T> = ...;
bar(y);
}
Here, the call to bar
should be disallowed because the call X::foo()
cannot be resolved because foo
is static.
I see three options:
- Require no static methods in object-safe traits (this is restrictive, due to the constructor issue)
- Check that traits are 'static-safe' when doing the 'auto-impl' trick when binding a type variable to a trait (this is slightly unfortunate since the object-safety principle was supposed to mean no extra checks here, and instead only restricting the traits which could be used as objects)
- Abandon object-safety at object creation time and instead check object-safety and 'static-safety' when doing the 'auto-impl' (this would be a shame, I think object-safety is a good principle)
We could also relax the check on static methods where the method is provided rather than required, since there is then an implementation for a method when making a static method call. However, this would lose the usual, expected overriding behaviour (the provided method would always be called, even if the dynamic concrete type overrides it).
I think my favoured solution is option 1 with the provided method relaxation. I believe (though might be wrong here) that there is only benefit to having static constructor methods in a trait, rather than a concrete type, if there is a provided default method. So, the only cases which become inconvenient are where there is a provided constructor and this is overridden in a concrete type which is used to make an object.
See also discussion in rust-lang/rust#18527