Skip to content
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

Document how self: Self receiver types *actually* interact with object safety #1455

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions src/items/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ Object safe traits can be the base trait of a [trait object]. A trait is
* `Sized` must not be a [supertrait][supertraits]. In other words, it must not require `Self: Sized`.
* It must not have any associated constants.
* It must not have any associated types with generics.
* All associated functions must either be dispatchable from a trait object or be explicitly non-dispatchable:
* All associated functions must either be dispatchable from a trait object,
or be explicitly or implicitly non-dispatchable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that terminology feels weird, fn foo<T> is also "implicitly non-dispatchable", isn't it?

All associated functions must either be dispatchable from a trait object or must not be applicable to trait objects

Suggested change
or be explicitly or implicitly non-dispatchable.
or must not be applicable to trait objects.

* Dispatchable functions must:
* Not have any type parameters (although lifetime parameters are allowed).
* Be a [method] that does not use `Self` except in the type of the receiver.
Expand All @@ -81,13 +82,26 @@ Object safe traits can be the base trait of a [trait object]. A trait is
* [`Box<Self>`]
* [`Rc<Self>`]
* [`Arc<Self>`]
* [`Pin<P>`] where `P` is one of the types above
* [`Pin<P>`] where `P` is one of the types in this list (this also applies recursively)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could split this out to a "A type is a dispatchable receiver if it's one of: "

then we can say Pin<P> if P is a dispatchable receiver

* Not have an opaque return type; that is,
* Not be an `async fn` (which has a hidden `Future` type).
* Not have a return position `impl Trait` type (`fn example(&self) -> impl Trait`).
* Not have a `where Self: Sized` bound (receiver type of `Self` (i.e. `self`) implies this).
* Explicitly non-dispatchable functions require:
* Have a `where Self: Sized` bound (receiver type of `Self` (i.e. `self`) implies this).
* Not have a `where Self: Sized` bound.
* Explicitly non-dispatchable functions must:
* Have a `where Self: Sized` bound.
* Implicitly non-dispatchable functions must:
* Have the receiver type `Self` (i.e. `self`)
* Fulfill all the conditions of dispatchable functions, except for the receiver type.
Comment on lines +90 to +94
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Explicitly non-dispatchable functions must:
* Have a `where Self: Sized` bound.
* Implicitly non-dispatchable functions must:
* Have the receiver type `Self` (i.e. `self`)
* Fulfill all the conditions of dispatchable functions, except for the receiver type.
* Non-applicable functions have either:
* a `where Self: Sized` bound.
* the receiver type `Self` (i.e. `self`) and fulfill all the conditions of dispatchable functions, except for the receiver type.


Methods with `Self` receiver type are implicitly non-dispatchable
(“non-dispatchable” means that they cannot be called on trait object types), but
[might become dispatchable in future versions of Rust](https://github.com/rust-lang/rust/issues/48055).
Comment on lines +96 to +98
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Methods with `Self` receiver type are implicitly non-dispatchable
(non-dispatchable means that they cannot be called on trait object types), but
[might become dispatchable in future versions of Rust](https://github.com/rust-lang/rust/issues/48055).
Methods with `Self` receiver type are currently non-dispatchable
("non-dispatchable" means that they cannot be called on trait object types), but
[might become dispatchable in future versions of Rust](https://github.com/rust-lang/rust/issues/48055).

Currently, unsized function arguments are not supported in Rust, so such methods cannot be
implemented for unsized types, which means providing a function body as default implementation
for the method is not possible, nor are (manual) implementations of the trait for unsized types
possible. Implicitly non-dispatchable methods serve the future-compatibility of your trait,
if unsized function arguments become supported in future versions of Rust,
as removing an explicit `Self: Sized` bound later would constitute a semver-breaking API change.

```rust
# use std::rc::Rc;
Expand Down Expand Up @@ -120,16 +134,23 @@ trait NonDispatchable {
fn param(&self, other: Self) where Self: Sized {}
// Generics are not compatible with vtables.
fn typed<T>(&self, x: T) where Self: Sized {}
// `self: Self` functions cannot be dispatched,
// but this mighty change in the future
fn by_value(self); // default implementation is not supported
}

struct S;
impl NonDispatchable for S {
fn returns(&self) -> Self where Self: Sized { S }
fn by_value(self) {}
}
let obj: Box<dyn NonDispatchable> = Box::new(S);
obj.returns(); // ERROR: cannot call with Self return
obj.param(S); // ERROR: cannot call with Self parameter
obj.typed(1); // ERROR: cannot call with generic type
// `dyn NonDispatchable` is a dynamically sized type
// and does not implement the `Sized` trait
obj.returns(); // ERROR: cannot call the function due to its `Self: Sized` bound
obj.param(S); // ERROR: cannot call the function due to its `Self: Sized` bound
obj.typed(1); // ERROR: cannot call the function due to its `Self: Sized` bound
obj.by_value(); // ERROR: cannot call functions with dynamically sized arguments
```

```rust,compile_fail
Expand Down Expand Up @@ -162,8 +183,8 @@ let obj: Box<dyn TraitWithSize> = Box::new(S); // ERROR

```rust,compile_fail
// Not object safe if `Self` is a type argument.
trait Super<A> {}
trait WithSelf: Super<Self> where Self: Sized {}
trait Super<A: ?Sized> {}
trait WithSelf: Super<Self> {}

struct S;
impl<A> Super<A> for S {}
Expand Down