My own personal notes. Documents from the successive design meetings:
The ones that feel most crucial:
&
/&mut
/ref
/ref mut
and it will do the obvious thing;The ones that feel important to figure out the proposal:
&mut T
like it's &T
;&mut
pattern gives mutable access to the place inside it;&
pattern prevents mutable access to any place inside it;x: &T
, I can copy out the value by writing &
before the binding;mut
before it;Vaguer ones:
if let <pat1>(<pat2>) = <expr>
behaves the same as if let <pat1>(x) = <expr> && let <pat2> = x
;We mostly have mirroring and composition for patterns without &
/&mut
/ref
/ref mut
(which I would call "purely structural patterns"). It gets tricky with references.
We could also extend these to cover my other pattern work with axioms like:
Rc
to Arc
should require minimal code changes;NOTE: this has been superceded by further discussions; it is kept for posterity only.
I want to formulate match ergonomics in terms of types, ideally as a typing rule. The rule would look like: <pat> @ <expr> : <type>
so we can track which place is tested or used for bindings.
Given a pattern p
and a type T
, we start with p @ q: T
(where q
is a dummy scrutinee expression to) and apply the rules. This will either error or assign each binding to an expression and a type.
I implemented these rules and variations in a little tool: https://github.com/Nadrieril/typing-rust-patterns.
The expression is the only implicit state that is tracked. This is a feature: implicit state is confusing. This means we don't get match ergonomics 2024 rule 3.
Interestingly, the expression implicitly tracks the DBM: it can be either a place (corresponding to move
DBM), &<place>
(corresponding to ref
DBM) or &mut <place>
(corresponding to ref mut
DBM). Note that we only inspect the expression for bindings, never in the middle of a pattern.
Note: a rule looks like:
It is applied bottom-to-top. Read the rule like "to get bottom thing
, if side conditions apply, we need top things
to be (recursively) true". At most one rule should apply to any given case; if no rule applies, then the pattern is not valid for that type. The example derivations below should help understand how this works.
Compared to the rules in https://github.com/rust-lang/rust/issues/127559:
[&x]: &[&T] => x: &T
but not [&mut x]: &[&mut T]
;&[[&x]]: &[&mut [T]]
but not &[[x]]: &[&mut [T]]
;Some((&x, ref y)): &Option<(T, U)>
=> x: T
, y: &U
[&x]: &[&T]
=> x: &T
[&x]: &[&mut T]
=> x: &mut T
, move error[&&mut x]: &[&mut T]
=> x: T
[&mut x]: &mut [&T]
=> x: &T
[&mut x]: &[&mut T]
=> type error&[[x]]: &[&mut [T]]
=> x: &mut T
, borrow error&[[&x]]: &[&mut [T]]
=> x: T
, borrow error if we don't use simplification rules&[[&mut x]]: &[&mut [T]]
=> x: T
, borrow error if we don't use simplification rules[&ref mut x]: &mut [T]
=> x: &mut T
, borrow error