Various suggestions - empty impls, empty traits, struct field traits,

Hi!

I wanted to make a few suggestions / understand if there's already an RFC for any of these:

  1. Empty impls / Empty traits

When working with marker traits, I personally think it would be nice to be able to not supply the trailing block, as Rust currently allows for structs. e.g.:

trait MarkerTrait;
impl MarkerTrait for Entity;

vs

trait MarkerTrait {}
impl MarkerTrait for Entity {}

I personally find the former more readable (and subjectively, think it looks nicer)

  1. Struct field traits

I've seen a few questions around this in the past and a mention of a proposal, but wanted to give my two cents on why this would be useful.

Since Rust traits only allow for functions rather than fields, grouping functionality can become a bit more difficult in the case where you want a struct to have a certain shape, e.g. for serialization, to later be able to use it for a specific purpose.

For instance if I wanted to set an invariant for records that can be stored in a DB where they must have an ID field and a few other fields that must exist to be able to later query said records, I could theoretically define a trait with functions that supply those fields, but that doesn't actually guarantee anything in terms of the record shape.

  1. Allow referring to associated consts in traits

This one is similar to the previous point, I think it could be useful to be able to blanket impl a trait which rather than a function, requires the implementing type to define an associated const of a certain type, to be able to implement functionality once (e.g. in a function on the same trait which uses said const) rather than implementing the same function for each struct.

Thanks!

2 Likes

Note that struct Foo; and struct Foo {} do different things! The former adds const Foo: Foo = Foo {};, but the latter doesn't.

Since there isn't a similar thing for impls and traits, I don't think it's worth having another way to write the same thing.

(I do think ; looks a bit nicer than {}, but not enough to need to support both nor to force everyone to transition over an edition.)

9 Likes

Thanks @scottmcm! I wasn't aware of the difference.

Regarding the point on an edition - not sure I understand why one would be needed, would this not be backwards compatible with existing code?

Are new editions not normally created in cases where for example a new keyword could break existing variables with the same name?

Thanks!

Regarding the const creation, was the decision to allow the short form for convenience or for another reason?

(Perhaps "unit" enum variants (with no data) use the same machinery, since enum variants behave like structs syntactically?)

Thanks!

Edit: removed part of the question since it wasn't relevant / correct

If you're curious, see https://rust-lang.github.io/rfcs/1506-adt-kinds.html#unit-structs

It would absolutely be backward compatible to support both. What I was referring to there is that I'm not a fan of both, so if we wanted to remove {} support (to no longer have two ways of writing it) then that would take an edition.

1 Like

I think it'd be nice to have. It's not super important, but I remember being slightly annoyed that unsafe impl Send for Type {} requires useless brackets, and that my editor and rustfmt have different opinion on their placement.

6 Likes

I see, although I'm not sure (maybe missed it in the link) whether struct Foo; has a const to make usage easier (in which case it's a change for convenience on top of a change for convenience, which theoretically means two different ways to do the same thing has "precedent"), or if it has another reason for the difference in which case it's not actually two ways to do the same thing.

Mainly asking out of curiosity, although personally I'm not sure I'd be against having both, there's other examples (like where) where there's multiple simultaneous options

The post itself has three (unrelated) points by the way, in case it's unclear that the latter two are not a continuation of the first

This is already possible, unless I misunderstood you.

2 Likes

You're right!

The post originally stated a blanket impl for a struct with a const which I don't think is possible but perhaps I didn't find it, but I edited the description and dropped the word impl so you're entirely correct that it exists (I also wasn't aware of this existing and it solves my original issue, but perhaps impl for a struct with const x still makes sense)

Edited the original

I would consider this an implementation detail. The const item is just added to also allow omitting the {} at the usage site. Other than that, it doesn't affect behavior in any way; when importing a unit struct, the const item is implicitly imported as well.

Note that enum variants also allow omitting the {}, which is equivalent to declaring an associated const with the same name, except that this doesn't actually work.

Personally I'm not opposed to allowing ; in addition to {} after traits and impls. The main question is where we draw the line.

  • Do we also want to allow ; after functions with an empty body? No, because that means something very different in a trait.
  • Do we want to allow it after empty enums and unions? Maybe for consistency.
  • expressions such as if, while, loop and for? Probably not, although there is precedent in other programming languages.
  • macro calls? Maybe.
2 Likes