Tagged unions never went away, it's just that, as you mention, C++ style OOP became very popular and they worked the other way around: instead of having a single function that matches the tag to figure out what to do with the data, you ship data-specific functions straight inside the object (virtual methods).
In theory you can have both of course, and C++ now has support for variants (because we know C++ tries to be come a superset of all programming languages in existence). It's still a bit clunky to use though, especially because of the lack of "match" support in the language.
But I think a problem is that it's easy to mess up tagged union in low level, non-managed languages. Rust's enum work great, but that's thanks to the very strong type system and borrow checker which makes them effectively impossible to mess up in safe code.
In C or C++ it can become messy and hard to debug. In C you don't really have a choice, so you do it anyway but in C++ virtual methods and inheritance are usually a bit easier to manage.
In theory you can have both of course, and C++ now has support for variants (because we know C++ tries to be come a superset of all programming languages in existence). It's still a bit clunky to use though, especially because of the lack of "match" support in the language.
But I think a problem is that it's easy to mess up tagged union in low level, non-managed languages. Rust's enum work great, but that's thanks to the very strong type system and borrow checker which makes them effectively impossible to mess up in safe code.
In C or C++ it can become messy and hard to debug. In C you don't really have a choice, so you do it anyway but in C++ virtual methods and inheritance are usually a bit easier to manage.