That's definitely my experience, I never used a language featuring sum types before I started learning rust a few months ago but now I miss them as soon as I go back to C.
For those of you who are not yet enlightened here's a couple examples: I had to implement a time counter struct. It was either counting, in which case I would only store the date corresponding to the time 0 (and I would compute the current counter value by substracting the current time to this date instead of having to update the counter every second) or halted in which case I would just store the last counter value.
So my first try was:
struct Counter {
counting: bool,
/// When counting is true
tzero: u32,
/// When counting is false
last_count: u32,
}
Now of course, that's error prone and wasteful. Wasteful because you store two u32 when only one of them will be used at any given time and error prone because you have to be careful to always correctly check the value of counting before using one of the other values. You could save some space by using a single "tzero_or_last_count" u32 but that makes it even more error prone.And then I saw the light:
enum Counter {
Counting(u32),
Halted(u32),
}
This does exactly what I needed: No wasted space since this only takes the space for a single u32 and then a little more for the tag, the equivalent of my counting flag above but handled by the language directly. And it's so much nicer to handle: the language won't let me access the underlying integers unless I match the enum which forces me to check. What was an implicit convention left for the coders to enforce is now checked by the compiler.An other example I have is the following:
enum CacheState { Valid(Cache), Dirty, }
I can only access the Cache when it's not Dirty. In C or C++ you could get similar results by using encapsulation and have accessor functions that check the dirty flag but you have to implement and debug all that by yourself. With sum types it's built directly into the language.