Try   HackMD

Arbitrary self types options

Constraints and goals

  • We want to permit adding additional methods to *mut T.
    • Requires: libs team is able to add methods without disruption to the ecosystem or existing programs.
  • We want to permit adding self: MyRc<T> methods.
  • We need code to continue compiling, with same semantics, in rustc vN and vN+1.
  • We need trait Receiver separate from Deref because not all smart pointer types can support deref (for good reasons).
  • We need a way to define methods that do not form a (safe) Rust reference when called.
  • We need to be forward-compatible with ways to invoke through dyn (not covered in the RFC).

All options

  • Uses Receiver trait to identify method candidates. This allows fn foo(self: P<Self>) for custom smart pointers.
  • Blanket impl of Receiver for T: Deref.

Option 0: Support for newtype wrappers, no changes to resolution order, methods prohibited by convention alone

  • Use the current outer-first resolution order, i.e. prefer Box::foo(self) over fn foo(self: Box<Self>)
  • Largely resolves the needs of Rust-for-Linux (KernelArc<T>) and cross-language interop (CppRef<T>).
  • Possibility to define a wrapper Raw<T>: Receiver in core to cover the unsafe Rust use-case.
  • New methods on an outer type (e.g. Box) run the risk of shadowing existing methods on an inner type. Anyone implementing Deref or Receiver should not add methods, just like we don't add methods for Box and friends. But this is not enforced.
  • Believed to be forward-compatible with options 4 and 5.

Option 1: Support for newtype wrappers, with "inner-first" resolution order

  • Use an inner-first resolution order, i.e. prefer fn foo(self: Box<Self>) over a foo method defined on Box<T>.
  • Largely resolves the needs of Rust-for-Linux (KernelArc<T>) and cross-language interop (CppRef<T>).
  • Possibility to define a wrapper Raw<T>: Receiver in core to cover the unsafe Rust use-case.
  • Not forward-compatible with options 4 or 5.

Option 2: Support for newtype wrappers, with a hard error on resolution ambiguity

  • Method resolution gives an error when there is ambiguity between a Receiver-resolved method like fn foo(P<Self>) and some other method.
  • Largely resolves the needs of Rust-for-Linux (KernelArc<T>) and cross-language interop (CppRef<T>).
  • Possibility to define a wrapper Raw<T>: Receiver in core to cover the unsafe Rust use-case.
  • Similar to current behavior (option 0), but we'd probably error on a couple more cases where we currently pick either the outer or inner (we always prioritize by-value calls over by-reference calls).
  • Believed to be forward-compatible with options 4 and 5.

Option 3: Support for raw pointers, do not allow new methods on raw pointers

  • Option 0, 1 or 2, with the addition that we make *mut T (and probably NonNull<T> and Weak<T>) implement Receiver.
  • Supports the use-cases in unsafe Rust, as well as RfL and cross-language interop.
  • We decide never to add new methods to raw pointers, NonNull or Weak again.

Option 4: Support for raw pointers, add new method resolution rules with a warning

  • In case of a method resolution ambiguity involving a P<Self> method, we pick the method defined on the inner type and show a warning.
  • Make *mut T (and probably NonNull<T> and Weak<T>) implement Receiver.
  • Supports the use-cases of unsafe Rust, as well as RfL and cross-language interop.
  • This permits us to add new methods to raw pointers, NonNull and Weak though this would still require users to resolve warnings so we might still not want to.
  • This can still cause surprises (in the "race condition" scenario explained below) since warnings are suppressed when compiling a downstream crate.

Option 5: Support for raw pointers, add new method resolution rules with no warnings

  • In case of a method resolution ambiguity involving a P<Self> method, we silently pick the the method defined on the inner type.
  • Make *mut T (and probably NonNull<T> and Weak<T>) implement Receiver.
  • Supports the use-cases of unsafe Rust, as well as RfL and cross-language interop.
  • This permits us to add new methods to raw pointers, NonNull and Weak.
  • This can silently change which method is called if a crate defines fn foo(self: NewBox<MyType>) while in parallel the crate that defines MyBox adds a foo method (the "race condition" scenario).