Best way to represent 'constants' that are bytes

Hi All,

Somewhat new to rust, but trying to figure out the best way to encode enums that come from a external specification (and have defined byte values). Particularly I'm trying to represent MIDI notes/commands.

My first pass is here https://github.com/btrepp/usbd-midi/blob/master/src/notes.rs. Though I dislike how many items are in the enum, and also that defining flats seems a little awkward.

Though I'm wondering if I would be better off defining const u8s, and then maybe making a higher level type?, and writing functions to parse/format that into u8s?.

Particularly curious in how best to extend it to data in the midi spec that uses the first 4bits for one purpose, and the second 4 for something else.

Thanks.

I understand your aversion of what it looks like, but at some point if you want unique identifiers for 100+ items, there's only so much you can do. Putting the declaration in it's own file, you don't have to look at it everyday either ;). A few ideas might improve it slightly:

  • don't necessarily put everything on a new line. You could just list them per octave, making it a bit more digestible.
  • create a macro to generate them programmatically. It's not necessarily more readable tbh, but it's probably shorter.

For the flat's, unfortunately there isn't a way to alias enum variants right now. I think your putting them in impl Note is a pretty neat way of making them consistent with the enum from the perspective of client code.I do think you can specify the #[allow(non_upper_case_globals)] on the impl Note just once, so you don't have to repeat it.

For storing 4bit values, maybe this helps:
https://docs.rs/ux

I'm under the impression that you can't actually define sub-byte size layouts in rust. In that case you can store several values in a bigger type and create getters/setters which read the correct bits with bitwise operators.

An alternative representation is to use a new-typed integer and define consts for each of the variants, this is most helpful(/necessary) when the specification is intended to be extended in the future. You can see an example here where you can create a Tag of any value, but there are defined constants that can be used for creating/matching known values.

2 Likes

I think the rule of thumb is, if the list of possible variants is exhaustive, use an enum. If it's not, use a newtype with constants. In this case, it seems that the enum covers all possible values allowed by the specification, so it makes sense to use enum here. You can imagine someone matching on all variants, for example, when writing a fancy Display implementation. You can even use your flat constants in place of the main enum variants, and the compiler is smart enough to verify that the match arms are still exhaustive. So I'd say your approach is fine.

For your second question about 4-bit values, see bitfield crate.

1 Like

unrelated to the question directly, but from what i remember from MIDI all note numbers 0..127 are valid—so if you use an enum it needs to be exhaustive over that range