Skip to content

Commit 95594f7

Browse files
committed
Add more alternatives
1 parent de6a4cf commit 95594f7

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

text/0000-supertrait-item-shadowing-v2.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,58 @@ This behavior can be surprising: adding a method to a sub-trait can change which
5959

6060
If we choose not to accept this RFC then there doesn't seem to be a reasonable path for adding new methods to the `Iterator` trait if such methods are already provided by `itertools` without a lot of ecosystem churn.
6161

62+
## Only doing this for specific traits
63+
64+
One possible alternative to a general change to the name resolution rules would be to only do so on a case-by-case basis for specific methods in standard library traits. This could be done by using a perma-unstable `#[shadowable]` attribute specifically on methods like `Iterator::intersperse`.
65+
66+
There are both advantages and inconvenients to this approach. While it allows most Rust users to avoid having to think about this issue for most traits, it does make the `Iterator` trait more "magical" in that it doesn't follow the same rules as the rest of the language. Having a consistent rule for how name resolution works is easier to teach people.
67+
68+
## Preferring the supertrait method instead
69+
70+
In cases of ambiguity between a subtrait method and a supertrait method, there are 2 ways of resolving the ambiguity. This RFC proposes to resolve in favor of the subtrait since this is most likely to avoid breaking changes in practice.
71+
72+
Consider this situation:
73+
74+
- library A has trait `Foo`
75+
- crate B, depending on A, has trait `FooExt` with `Foo` as a supertrait
76+
- A adds a new method to `Foo`, but it has a default implementation so it's not breaking. B has a pre-existing method with the same name.
77+
78+
In this general case, the reason this cannot be resolved in favor of the supertrait is that the method signatures are not necessarily compatible.
79+
80+
[In code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b3919f7a8480c445d40b18a240936a07):
81+
82+
```rust
83+
#![allow(unused)]
84+
85+
mod a {
86+
pub trait Int {
87+
// fn call(&self) -> u32 {
88+
// 0
89+
// }
90+
}
91+
impl Int for () {}
92+
}
93+
94+
mod b {
95+
pub trait Int: super::a::Int {
96+
fn call(&self) -> u8 {
97+
0
98+
}
99+
}
100+
impl Int for () {}
101+
}
102+
103+
use a::Int as _;
104+
use b::Int as _;
105+
106+
fn main() {
107+
let val = ().call();
108+
println!("{}", std::any::type_name_of_val(&val));
109+
}
110+
```
111+
112+
Resolving in favor of `a` is a breaking change; in favor of `b` is not. The only other option is the status quo: not compiling. `a` simply cannot happen lest we violate backwards compatibility and the status quo is not ideal.
113+
62114
# Prior art
63115
[prior-art]: #prior-art
64116

0 commit comments

Comments
 (0)