Skip to content

Commit 480cc46

Browse files
committed
Arbitrary self types v2: shadowing semver break.
This is a pre-existing risk of semver breakage for types implementing Deref<Target=T>. It's made more apparent by the arbitrary self types v2 change, and will now generate errors under some circumstances. This PR documents the existing risk and the new ways it can be triggered. If it's seen as desirable to document the existing risk before Arbitrary Self Types v2 is stabilized, we can split this commit up into two. Part of rust-lang/rust#44874 Stabilization PR rust-lang/rust#135881
1 parent 730d997 commit 480cc46

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

src/doc/src/reference/semver.md

+131
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ considered incompatible.
8888
* [Minor: generalizing a function to use generics (supporting original type)](#fn-generalize-compatible)
8989
* [Major: generalizing a function to use generics with type mismatch](#fn-generalize-mismatch)
9090
* [Minor: making an `unsafe` function safe](#fn-unsafe-safe)
91+
* [Major: adding a potentially shadowing method](#fn-add-potentially-shadowing-method)
9192
* Attributes
9293
* [Major: switching from `no_std` support to requiring `std`](#attr-no-std-to-std)
9394
* [Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields](#attr-adding-non-exhaustive)
@@ -1883,6 +1884,136 @@ Making a previously `unsafe` associated function or method on structs / enums
18831884
safe is also a minor change, while the same is not true for associated
18841885
function on traits (see [any change to trait item signatures](#trait-item-signature)).
18851886

1887+
### Major: add a potentially shadowing method {#fn-add-potentially-shadowing-method}
1888+
1889+
If you have a type which implements `Deref<Target=T>`, you must not add methods
1890+
which may "shadow" methods in `T`. This can lead to unexpected changes in
1891+
program behavior.
1892+
1893+
```rust,ignore,skip
1894+
// MAJOR CHANGE
1895+
1896+
///////////////////////////////////////////////////////////
1897+
// Before
1898+
pub struct MySmartPtr<T>(pub T);
1899+
1900+
impl<T> core::ops::Deref for MySmartPtr<T> {
1901+
type Target = T;
1902+
fn deref(&self) -> &Self::Target {
1903+
&self.0
1904+
}
1905+
}
1906+
1907+
///////////////////////////////////////////////////////////
1908+
// After
1909+
pub struct MySmartPtr<T>(pub T);
1910+
1911+
impl<T> core::ops::Deref for MySmartPtr<T> {
1912+
type Target = T;
1913+
fn deref(&self) -> &Self::Target {
1914+
&self.0
1915+
}
1916+
}
1917+
1918+
impl<T> MySmartPtr<T> {
1919+
fn method(&mut self) {
1920+
println!("A");
1921+
}
1922+
}
1923+
1924+
///////////////////////////////////////////////////////////
1925+
// Example usage that will break.
1926+
use updated_crate::MySmartPtr;
1927+
1928+
struct SomeStruct;
1929+
1930+
impl SomeStruct {
1931+
fn method(&self) {
1932+
println!("B");
1933+
}
1934+
}
1935+
1936+
fn main() {
1937+
let mut ptr = MySmartPtr(SomeStruct);
1938+
ptr.method(); // prints B before, A after
1939+
}
1940+
```
1941+
1942+
Note that the shadowing and shadowed methods receive `self`
1943+
slightly differently: `&self` and `&mut self`.
1944+
That's because Rust searches for methods first by value, then by `&`, then
1945+
by `&mut T`, then by `*const T`. Rust stops the search
1946+
when it encounters a valid method, and so methods later in this order may
1947+
be shadowed by methods encountered earlier.
1948+
1949+
This is only a compatibility risk if the `Deref` target is
1950+
beyond your control. If your type implements `Deref` to another type where
1951+
you can fix the available methods, you can ensure no shadowing
1952+
occurs. An example is that `PathBuf` implements
1953+
`Deref<Target=Path>`.
1954+
1955+
For types which do implement `Deref` with an arbitrary target,
1956+
it's bad practice to add methods: add associated functions instead. This is
1957+
the pattern used by Rust's standard library smart pointer types, such as
1958+
`Box`, `Rc` and `Arc`.
1959+
1960+
Similar shadowing risks occur for a type implementing
1961+
`Receiver<Target=T>`. If you have a type which implements either
1962+
`Receiver<Target=T>` or `Deref<Target=T>` it may be used as a method receiver
1963+
by `T`'s methods. If your type then adds a method, you may shadow methods in
1964+
`T`. For instance:
1965+
1966+
```rust,ignore,skip
1967+
// MAJOR CHANGE
1968+
1969+
///////////////////////////////////////////////////////////
1970+
// Before
1971+
pub struct MySmartPtr<T>(pub T);
1972+
1973+
impl<T> core::ops::Receiver for MySmartPtr<T> {
1974+
// or Deref
1975+
type Target = T;
1976+
}
1977+
1978+
///////////////////////////////////////////////////////////
1979+
// After
1980+
pub struct MySmartPtr<T>(pub T);
1981+
1982+
impl<T> core::ops::Receiver for MySmartPtr<T> {
1983+
// or Deref
1984+
type Target = T;
1985+
}
1986+
1987+
impl<T> MySmartPtr<T> {
1988+
fn method(self) {
1989+
1990+
}
1991+
}
1992+
1993+
///////////////////////////////////////////////////////////
1994+
// Example usage that will break.
1995+
use updated_crate::MySmartPtr;
1996+
1997+
struct SomeStruct;
1998+
1999+
impl SomeStruct {
2000+
fn method(self: &MySmartPtr<Self>) {
2001+
2002+
}
2003+
}
2004+
2005+
fn main() {
2006+
let ptr = MySmartPtr(SomeStruct);
2007+
ptr.method(); // error: multiple applicable items in scope
2008+
}
2009+
```
2010+
2011+
When types like this are being used as method receivers, Rust endeavours to
2012+
do additional searches and present errors in simple cases, e.g. shadowing of
2013+
`&self` by `self` with inherent methods. This is better than invisible
2014+
behavior changes - but either way it's a compatibility break. Avoid adding
2015+
methods if you implement `Deref` or `Receiver` to an arbitrary target.
2016+
18862017
### Major: switching from `no_std` support to requiring `std` {#attr-no-std-to-std}
18872018

18882019
If your library specifically supports a [`no_std`] environment, it is a

0 commit comments

Comments
 (0)