# Deref Patterns, requirements and Syntax Tracking Document Discussion on irlo: https://internals.rust-lang.org/t/somewhat-random-idea-deref-patterns/13813 Discussion on Zulip: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Deref.20patterns Older irlo discussion: https://internals.rust-lang.org/t/pre-pre-rfc-match-ergonomics-for-container-types-restricted-method-calls-in-patterns/13371 Also discussed on the box patterns issue: https://github.com/rust-lang/rust/issues/29641 ## Goal Be able to match through a `Deref` or `DerefMut` smart pointer ergonomically. ```rust let x: Option<Rc<bool>> = ...; match x { Some(deref true) => ..., Some(x) => ..., None => ..., } ``` ## Syntax options - No keyword or sigil at all - No keyword or sigil for literals (e.g. matching `String` with `"hello"`), keyword or sigil for non-literals (matching `String` with `x` and having `x` work as type `&str`) - Only allow literals, disallow non-literals - Sigil `*`: `*<pattern>` - Sigil `&`: `&<pattern>` - Overloads the existing `&` pattern - Keyword `box`: `box <pattern>` - Reuses and generalizes `box_pattern` syntax - This would likely be confusing for non-`Box` types - Keyword `deref`: `deref <pattern>` - This would require reserving a new keyword - Keyword would confict with the exact method this would work with. ## Constraints - this affects match exhaustiveness. If we don't restrict the `Deref` impls, then it is unsound to consider the following as exhaustive: ```rust let x: Option<SomePointer<bool>> = ...; match x { Some(deref true) => ..., Some(deref false) => ..., None => ..., } ``` - we can't generally commit to only `deref`ing once per subpattern, because e.g.: ```rust match x { Some(deref 0) => ..., Some(deref mut 1) => ..., Some(deref 2) => ..., } ``` - Note: this case could be implemented as a single `deref_mut`, then reborrowing. However, this would not work in a case like this: ```rust match &mut x { Some(deref v@0..3) => ..., // (1) Some(deref mut v@4..7) => ..., // (2) v@&None => ..., // (3) Some(deref v@8..11) => ..., // (4) } ``` - The above requires at least one deref_mut and one deref. Depending on the `Deref` impl, the shared borrow in pattern 3 may invalidate the mutable reference used in 1 and 2 (at the very least, for wrapper types like `ManuallyDrop`) - However, when `x` is immutable, it should be valid to only deref once. - if we allow side-effecting `Deref` impls, then people may start depending on the number and order of the `deref` calls, like they depended on drop order even if it was supposed to be unspecified. - Lang Team only wants "pure" `Deref` impls. But what about impure `Deref` that are idempotent (like `Lazy`/`SyncLazy`) - if we want the "no syntax" option, the type of `x` in `Some(x)` and `Some(x @ true)` could be different - that already happens with `&` and match ergonomics I think - some types can both be destructured and have a `Deref` impl - e.g. `Cow` and `AssertUnwindSafe` - outright ambiguous in the "no syntax" case: ```rust let cowcow: Cow<Cow<bool>> = ... match &cowcow { Cow::Owned(x) => {} // what's the type of `x`? } ``` - How would exhaustive matching handle mixing deref patterns with destructuring ```rust let unwind_safe: AssertUnwindSafe<bool> = ...; match &unwind_safe{ AssertUnwindSafe(true) => {}, deref false => {}, /* Would the above be considered exhasutive */ } ``` - Probably would be impossible for user-defined types; may be difficult for little advantage for standard library types (aside from adding `#[lang]` items to `Cow` and `AssertUnwindSafe`). - What about Cow when `T::Owned` is not `T` (`str`, `[T]`, custom `ToOwned` impl) - `DerefMove` still isn't a thing, yet we'd like to accomodate moving out of smart pointers in patterns in the future - so we need to be careful when things are owned to not default to taking `ref`s where in the future we would want to move out ```rust let x: Box<Option<bool>> = ... match x { Some(ref x) => {} // ok Some(ref mut x) => {} // ok Some(x) => {} // should error for now } ``` - mutability propagates backwards: the choice of `deref` vs `deref_mut` depends on subpatterns ```rust let x: Box<Option<bool>> = ... match x { Some(ref x) => {} Some(ref mut x) => {} } ``` - what happens if we mix `ref` and `ref mut` in a same pattern? Probably using `deref_mut` is ok. ## Possible solutions - Restrict to only a chosen set of Standard Library types - Current List: - Language-level References: `&T` and `&mut T` - Do we need to allow language-level references in deref patterns (absent generic deref patterns)? - Smart Pointers: `Box`, `Arc`, `Rc` - Precedent in Special Casing these in the standard library, with reciever types - Growable slice collections: `Vec`, `String`, `OsString`, `CString`, `PathBuf` - Vec and String also would be nice, since the deref targets (`[T]` and `str` resp.) - Mutex/RefCell guards: `Ref`, `RefMut`, `MutexGuard`, `RwLockReadGuard`, `RwLockWriteGuard` - Wrapper types: `IOSlice`, `IOSliceMut`, `ManuallyDrop`, `AssertUnwindSafe` (note: AssertUnwindSafe can be destructured) - ManuallyDrop in particular would be nice. - `Pin<P>` where `P` is a qualifying type - Likewise to `Box`, `Arc` and `Rc`, this has precedent in being special-cased in the language. - Could `Lazy` and `SyncLazy` qualify? - The implementations are not pure (can run arbitrary user-provided code), but are idempotent wrt. structure and address (and side effects) - Probably - Could `Cow` qualify? - `Deref` is idempotent wrt. structure and address - ~~`DerefMut` is not. The address stability question cannot be resolved. Additionally, it can invoke a user-provided `Clone`, which is a safe trait and may not produce a structurally equivalent value.~~ `Cow` does not implement `DefefMut` so this is not an issue - ~~Only an issue when mixing `Deref` and `DerefMut` accesses in patterns~~ - `Cow` can qualify for deref patterns, provided a `DerefMut` impl is not added. - Initial Project will persue this solution, providing the list of requirements for a standard library type to qualify, as well as propose an initial list - The standard library implementation would provide an unstable trait or attribute or otherwise (at it's option), applied to all qualifying types - Require a `_` pattern for the match to count as exhaustive ```rust let x: Option<SomePointer<bool>> = ...; match x { Some(true) => ..., Some(false) => ..., Some(_) => ..., None => ..., } ``` - people might rely on the order and number of `deref` calls, which would limit future changes - An unsafe `DerefPure` trait that restricts `deref`. - to make exhaustiveness sound, we need idempotency: successive calls must return "the same thing" - need at least structural stability between `deref{,_mut}` calls without an intervening access through `&mut` to the pointer or pointee, other than a `deref_mut` call. - ~~this disallows `DerefMut` for `Cow` since it must `clone` which may not preserve structural stability~~ Moot point, `Cow` is not `DerefMut` - unless we can restrict to structural clones - either with a new trait - or allowing only auto-derived `Clone` impls, akin to how only auto-derived `PartialEq` impls are allowed for constants in patterns - unless we disallow mixing `deref` and `deref_mut` on a same pointer - might also want address stability without an intervening move or access to the pointer through `&mut`. - would be useful for other optimizations too - could have stronger requirements like outright purity - would prevent `Lazy` - unsure what the benefits are - Some requirements are novel for unsafe traits. - restrict to `const` `deref` impls - Probably not enough to make exhaustiveness sound - could modify a `&mut` ref and thus not be idempotent - maybe enough if we don't allow `DerefMut`? - May be overly restrictive (e.g. a `Lazy` that reads a file) - Currently disqualifies all standard library smart pointers, due to not permitting raw pointer dereference.