Structs with similar fields

Hi,

Imagine that I have two structs:

struct Bar1 {
    a: i32,
    b: i32,
    c: i32
}
struct Bar2 {
   a: i32,
   b: i32,
   d: i32
}

The fields a and b appears in both, so it would make sense to somehow group a and b into a single expression and include it automatically in both structs. The compiler should then reconstruct the two structs for me.
In C I would use a preprocessor macro like:

#define SHARED_FIELDS \
    int a, \
    int b
struct Bar1 {
    SHARED_FIELDS,
    int c
}
struct Bar2 {
    SHARED_FIELDS,
    int d
}

How to do it properly in Rust?
I read that serde is able to achieve this by flattening. I checked the serde page about flattening, but I cannot get it to work. Here is a link to the playground: Rust Playground

Btw. this is the first time I use the serde crate. I do not know whether It should derive (de)serialize here, but I was on the example page. Does not seem to help though.

Composition is generally how you would handle this in Rust.

The serde attributes effect the serialized form (and deserializing there from), and not the structure of the Rust types themselves. In the Rust types, the a and b subfields are still nested in a foo: Foo field.

3 Likes

Thanks for your reply.
If I understand you correctly, Serde is not doing anything for me here. And you suggest, that I should simply include the Foo struct in the Bar structs? That seems unnecessarily restrictive. Is there no simple way to achieve what is so simple in C?

What's restrictive about it? Rust isn't as adverse to creating new structs as C.[1]

Here's one alternative where everything is flat. It doesn't scale to multiple additional fields though.


  1. Making language X centric data structures and program patterns in language Y leads to unidiomatic patterns more generally. ↩︎

2 Likes

You are right that "Language X can do it, why can't language Y" is not a clever thing to say.
I just thought that the problem of structs sharing fields is a common problem, so there might be a simple solution without composition. I think I will see if I can properly group my data and compose everything nicely in structs.
Thank you for your help.

There is a simple way with a macro: Rust Playground

You can tune the syntax to your liking.

Generally, if structs share a group of fields I do not see a reason not to group them in a struct. Composition is most of the time the simpler solution.

A bit nicer syntax (requires a comma after last field still): Rust Playground

2 Likes

There is almost surely a way to macro up what you suggested, it's just not that idiomatic. Looks like @Bruecki just posted one in fact.


I was casually pondering why you might consider the idiomatic way to be so restrictive. I'm still not sure, but one thing I thought of is maybe you think that the layout of the types would be related, like a pointer to Bar1 or Bar2 could be trivially cast to a pointer to Foo.

Let's arbitrarily assume that's what you were thinking of. That doesn't translate to typical Rust as the default layout of structs is not deterministic. If you want guarantees like that, you have to use something like repr(C).

Moreover, if you're not doing FFI, there's less of a point of having such implicit (field-based) layout compatibility, because you're not punning pointers all over the place. If you wanted to access the shared fields of such structs, you'd probably use AsRef<Foo> or such rather than unsafe methods such as pointer casting. Note that using AsRef<_> in this way would depend on composition, and isn't possible with the flattened form.[1]


Maybe that's not at all what you were thinking of, but what I'm trying to get at is that grouping fields explicitly in Rust is probably less restrictive than flattening everything, due to the different capabilities (such as traits like AsRef), the safe/unsafe distinction and stricter typing in the safe world, different base assumptions of the language (e.g. default layout determinism), and so on.

Rust isn't a subset of C or vice-versa. You may hold some C assumptions that don't hold in Rust. I know I did when I started learning Rust (and wrote some broken programs as a result). If you don't have some specific, semantic reason for flattening your data structures, I suggest using composition in Rust instead.


  1. Without more hoops like repr(C) and some unsafe or such, anyway. ↩︎

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.