• azdle@news.idlestate.orgOP
      link
      fedilink
      arrow-up
      4
      ·
      1 day ago

      I’m curious, have you used Rust much? Most of those changes just feel like “rust should be more familiar to me” changes.

      Also:

      As Rust 2.0 is not going to happen, Rust users will never get these language design fixes

      Isn’t necessarily true for most of your suggestions. Since most of them are just changes to syntax semantics and not language semantics they could be made in an edition.

    • TehPers@beehaw.org
      link
      fedilink
      English
      arrow-up
      4
      ·
      2 days ago

      Interesting perspective. Not sure I agree with most of the suggestions though.

      Some of the earlier ones remind me of C#'s records. Were they inspired from them?

      Some of the later ones just feel like Go to me.

      I like the idea of dropping syntax for ranges. It does feel like the syntax just leads to confusion.

      Named parameters are problematic because of parameter names becoming significant to the API. See Python’s * and / in parameter lists (like def foo(a, *, b) for example).

      • soc@programming.dev
        link
        fedilink
        English
        arrow-up
        2
        arrow-down
        1
        ·
        2 days ago

        Some of the earlier ones remind me of C#'s records. Were they inspired from them?

        No, that stuff is much much older.

        Named parameters are problematic because of parameter names becoming significant to the API. See Python’s * and / in parameter lists (like def foo(a, *, b) for example).

        I think the name problem is overblown, you can always have an annotation to facilitate name changes.

    • shape_warrior_t@programming.dev
      link
      fedilink
      English
      arrow-up
      11
      ·
      3 days 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.

      • soc@programming.dev
        link
        fedilink
        English
        arrow-up
        3
        ·
        2 days ago

        I went the “only let introduces bindings” route, and I’m pretty happy so far:

        if (left.next(), right.next())
        ... is (Some(let l), Some(let r)) { /* use l and r */ }
        ... is (Some(let l), None       ) { /* use l       */ }
        ... is (None,        Some(let r)) { /* use r       */ }
        ... is (None,        None       ) { /* use nothing */ }
        }
        
      • TehPers@beehaw.org
        link
        fedilink
        English
        arrow-up
        2
        ·
        2 days ago

        I completely forgot that unit structs/variants define their own associated consts. I wonder if in patterns the type can be used instead of the associated const though? That might resolve a lot of the headache. It’d mean changing the way the ident is resolved to looking in the type namespace though.

        const <block> already works as a pattern I believe? That could be used instead for constants.

        Literals would always work in-place as constant expressions.

    • TehPers@beehaw.org
      link
      fedilink
      English
      arrow-up
      6
      ·
      3 days 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.