--- title: "Arbitrary self types options" tags: ["T-lang"] date: 2024-02-28 discussion: https://rust-lang.zulipchat.com/#narrow/stream/410673-t-lang.2Fmeetings/topic/Design.20meeting.202024-02-28 url: https://hackmd.io/eXfrzrR7T1-3HVh6w5JMqA --- # 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).