-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conventions for the use of Deref
and DerefMut
#25
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
- Start Date: 2014-03-31 | ||
- RFC PR #: | ||
- Rust Issue #: | ||
|
||
# Summary | ||
|
||
The new `Deref` and `DerefMut` traits can enable more expressive APIs but | ||
create some design caveats that should be thought out sooner rather than later. | ||
This RFC will both change the APIs of current first-party types implementing | ||
these traits and serve as a set of conventions for future implementations. | ||
|
||
This was discussed previously in mozilla/rust#13126. | ||
|
||
# Motivation | ||
|
||
`Deref` and `DerefMut` are implemented by the various smart pointers in the | ||
standard library. Some, like `std::cell::Ref` and `sync::Arc` consist of | ||
nothing but `Deref`, `DerefMut` and `Drop` impls, and act just like a normal | ||
copy of the wrapped type would. Others, like `std::rc::Rc` and | ||
`sync::MutexGuard` have implementations of `Deref` and `DerefMut` as well | ||
as other methods or public fields. This poses a couple of problems: | ||
|
||
* It makes the types confusing to work with. When writing code that uses an | ||
`Rc<MyType>` or `MutexGuard<MyType>`, you end up with code that looks almost | ||
like code that uses a `MyType` directly except that a new field like `cond` | ||
or a new method like `downgrade()` is usable. If `MyType` already has a field | ||
or method with the same name as one of these, the author will get mysterious | ||
type errors until the real reason is discovered and the code is changed to | ||
the slightly strange looking `(*foo).cond`. | ||
|
||
* A type that implements `DerefMut` and/or `Deref` with a generic parameter can | ||
never have public fields or methods added to it without breaking backwards | ||
compatibility. | ||
|
||
# Detailed design | ||
|
||
In general, types that implement `Deref` or `DerefMut` should not implement any | ||
other methods or have any public fields. Methods like `downgrade` will move to | ||
associated functions: `Rc::downgrade(foo)` instead of `foo.downgrade()`. Fields | ||
like `cond` will also be accessed via associated functions: | ||
`MutexGuard::cond(foo)` instead of `foo.cond`. | ||
|
||
There are some exceptions to this rule. For example, the entire *point* of `Rc` | ||
is to change the semantics of `clone`, so it would be silly for `Rc` to not | ||
implement `Clone` directly. The same goes for `Arc`. Implementations of | ||
operator traits that simply forward to the wrapped types are also probably | ||
fine. These exceptions should be uncommon - I'd imagine it would apply almost | ||
exclusively to core traits like `Clone`, `Eq`, `Ord`, etc. | ||
|
||
Specifically, the following methods and fields will need to be altered: | ||
``` | ||
std::rc::Rc::downgrade | ||
sync::Arc::downgrade | ||
sync::Arc::make_unique | ||
sync::MutexGuard::cond | ||
sync::RWLockWriteGuard::cond | ||
sync::RWLockWriteGuard::downgrade | ||
``` | ||
|
||
In addition, documentation should be added to `Deref` and `DerefMut` discussing | ||
these conventions. | ||
|
||
# Alternatives | ||
|
||
C++ avoids this issue by having separate `.` and `->` operators. `foo.bar()` | ||
calls the `bar` function on the smart pointer type and `foo->bar()` calls the | ||
`bar` function on the wrapped type. Rust could change to emulate this setup. | ||
This would be a truly enormous change. `->` could be limited to use with | ||
`Deref` and `DerefMut`, but that ghettoizes smart pointers and would also | ||
probably be a bit confusing. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -1 lets not introduce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having unconditional auto-deref is annoying when there is a specific method one wishes to call (particularly in generated code like I have a feeling that UFCS should have some influence on this RFC too. |
||
|
||
Leaving the standard library's smart pointers as is will both limit our ability | ||
to add to their APIs in the future and add unnecessary "gotcha's" to the | ||
standard library. | ||
|
||
# Unresolved questions | ||
|
||
I'm a bit undecided on where some of the associated functions should end up. | ||
For example, it may make more sense to have `Mutex::cond(foo)` than | ||
`MutexGuard::cond(foo)` since `MutexGuard` is more of an implementation detail | ||
than anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could go the C route and only apply auto-deref via
->
on all pointer types (i.e.(~int).foo()
would be trying to callfoo
on~int
, but(~int)->foo()
would attempt to auto-deref.)