Exploding structs

We'd like to see this in Rust one day:

#[derive(PartialEq, Eq)]
struct X {
  foo: i32,
  bar: i32,
}

#[derive(PartialEq, Eq)]
struct Y {
  bar: i32,
  baz: i32,
}

let y = Y { bar: 3, baz: 5 }
let x = X { bar: 1, Y { bar: foo, ..: _ }: y };
assert_eq!(x, X { foo: 3, bar: 1 });

This would be basically a merger of FRUs with struct unpacking. It's basically sugar tho, as you can do this today:

let y = Y { bar: 3, baz: 5 }
let Y { bar: y_bar, baz: y_baz } = y;
drop(y_baz);
let x = X { bar: 1, foo: y_bar };

but it would make sense if we ever got subslice subpatterns (from Uh, what ever happened to subslice patterns? - #7 by Soni )

So, if I understand correctly, your suggestion is to allow pattern-matching inside {} constructors, and any names that are bound in the pattern are assigned into the field of the same name?

The code from your example doesn't seem very readable, but I could imagine wanting something like this:

fn some_fn() -> (i32, i32) { ... }

let x = X { (foo, bar): some_fn() };

That seems readable enough. On the other hand, I don't see a particularly strong motivation for it. In current Rust, I would write this:

fn some_fn() -> (i32, i32) { ... }

let (foo, bar) = some_fn();
let x = X { foo, bar };
4 Likes

Immediate reaction: couldn't figure what this means in 3 minutes of pondering..
It just keep hurting my brain trained on C and Java.

14 Likes

A previous conversation about a related feature: Pre-RFC: Array expansion syntax

Ignoring the mentioned syntax, this fits the sort of general "splatting" idea. Like I could imagine splatting a 2D point into a 3D point, providing the third coordinate explicitly.

1 Like

Why? What's the motivating example. What can be achieved that can't already?

Also, it just seems incredibly confusing and unreadable. I'd never want to work on code like this. Reminds me of all the arguments of why Perl was superseded by Python.

3 Likes

Mostly to unify a bunch of stuff under a common syntax and RFC. See also the other linked discussions.

I'm not sure that qualifies as a motivating goal. I don't think "unifying syntax" in and of itself has ever been recognized as a worthy goal in Rust. Is there some motivating example that demonstrates the utility of this feature beyond the status quo?

Personally we want the following features to be unified and defined under a single RFC:

  1. "Splatting" (array/tuple concatenation)

    [..: [1, 2], ..: [3, 4]] == [1, 2, 3, 4] // (also for b"foo", consts, and variables)
    (..: (1, 2), ..: (3, 4)) == (1, 2, 3, 4) // (not for b"foo" as that's not a tuple, but consts and variables)
    // not for str because that's !Sized
    

    these would just be sugar for [a[0], a[1], b[0], b[1]] and (a.0, a.1, b.0, b.1). (sorta. see Pre-RFC: Array expansion syntax for details.)

  2. Subslice subpatterns

    if let [..: [1, 2], rest @ ..] = array // (also for b"foo" and consts, but not variables)
    // not for tuples because we currently don't support subslice patterns for tuples anyway (since they don't work with ref/ref mut)
    

    these are hard to explain but they were originally part of the subslice pattern RFC. it got taken out due to issues with ranges, but there are no issues with ranges with this alternative syntax. this is the big thing that we want, especially as it relates to b"foo" and consts. it would make writing parsers in rust, especially recursive descent parsers with no lexer, significantly easier and more ergonomic. the current alternative is to break the b"foo"'s into b'f', b'o', b'o', which makes grepping and maintenance harder.

    also note that being able to pull array consts into slice patterns might lead to implementation difficulties, but is critical for the success of the feature.

  3. Current FRU syntax would be deprecated in favor of ..: for consistency with the previous 2 features.

It might make sense not to go "all in" and have full-on exploding structs. Rust devs do tend to be conservative about these things. But, exploding structs are simply a further extension of these features from our wishlist. Maybe something to put into a "future work" section of an RFC.

"Splatting" and subslice subpatterns are features that I would like to see in Rust, although the syntax you proposed doesn't work due to ranges and type ascription. A better syntax might be:

// splatting
let foo = [4, 5];
let bar = [1, 2, 3, foo @ ..];

// subslice subpattern
let [[a, b, c] @ .., rest @ ..] = bar;

(Although that would be somewhat inconsistent with other pattern syntax)

That being said, I don't see a strong motivation for allowing something similar for structs.

Type ascription has no use with RangeFull so that seems like it's fine to special case for.

let x = X { bar: 1, Y { bar: foo, ..: _ }: y };

That's kinda even more cursed than regular expression one-liners. Each such line of code would require a block comment above explaining what's really going on, and a bunch of assertions/tests below to make sure nothing's screwed up.

3 Likes