• shape_warrior_t@programming.dev
      link
      fedilink
      English
      arrow-up
      7
      ·
      11 hours ago

      There was a recent langdev Stack Exchange question about this very topic. It’s a bit trickier to design than it might seem at first.

      Suppose we require a keyword – say var – before all binding patterns. This results in having to write things like
      for (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {},
      which is quite a bit more verbose than the current
      for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}.
      Not to mention you’ll have to write let var x = 0; just to declare a variable, unless you redesign the language to allow you to just write var x = 0 (and if you do that, you’ll also have to somehow support a coherent way to express if let Some(x) = arr.pop() {} and let Some(x) = arr.pop() else {todo!()}).

      Suppose we require a keyword – say const – before all value-matching patterns that look like variables. Then, what’s currently

      match (left.next(), right.next()) {
          (Some(l), Some(r)) => {}
          (Some(l), None) => {}
          (None, Some(r)) => {}
          (None, None) => {}
      }
      

      turns into either the inconsistently ugly

      match (left.next(), right.next()) {
          (Some(l), Some(r)) => {}
          (Some(l), const None) => {}
          (const None, Some(r)) => {}
          (const None, const None) => {}
      }
      

      or the even more verbose

      match (left.next(), right.next()) {
          (const Some(l), const Some(r)) => {}
          (const Some(l), const None) => {}
          (const None, const Some(r)) => {}
          (const None, const None) => {}
      }
      

      and you always run the risk of forgetting a const and accidentally binding a new match-all variable named None – the main footgun that syntactically distinguishing binding and value-matching patterns was meant to avoid in the first place.

      Suppose we require a sigil such as $ before one type of pattern. Probably the best solution in my opinion, but that’s one symbol that can no longer be used for other things in a pattern context. Also, if you’re already using sigils before variable names for other purposes (I’ve been sketching out a language where a pointer variable $x can be auto-dereferenced by writing x), doubling up is really unpleasant.

      …So I can understand why Rust chose to give the same, most concise possible syntax for both binding and value-matching patterns. At least compiler warnings (unused, non-snake-case variables) are there to provide some protection from accidentally turning one into the other.

    • TehPers@beehaw.org
      link
      fedilink
      English
      arrow-up
      4
      ·
      16 hours ago

      As in using consts (or variables you think are consts) as refutable patterns? Yeah this was an oversight I’m sure.

      One option is an edition change requiring a const keyword, so

      match foo {
          const BAR => {},
          baz => {},
      }
      

      Right now they use a lint to try to warn the dev though.