I wouldn't call “use of OOP as it's described in basically every book” as “a strange hack”, though.
In Java “type erasure” works by passing around objects of “most-base type” into “type-erased” function with follow-up upcast.
You can do that in Rust, too, just would need to use explicit conversion into dyn Trait
and then use of core::any::Any to downcast on the extraction from collection.
That's not how how Rust collections work, but one can, most definitely, create Java-like collections with Box<dyn Trait>
or maybe Rc<dyn Trait>
and type erasure there would work in exactly the same way as in Java.
In what language type erasure removes some information from value?
What language loses bits from their integer representation and turns zero into one (or the other way around) when it does type erasure?
That would be strange and illogical. As was already noted type erasure tend to add some information to the value (to ensure that polymorphic code that have no idea about actual type of the value that it processes may process it).
That's not a requirement (lifetimes are stripped entirely without leaving any information about their existence), but, in general, type erasure keeps the value intact and then adds some runtime markups which are needed to “return the value” from it's typeerased state.
When you put something in an HashSet
you expect to get that value back intact whether said HashSet
uses type erasure (like in Java) or monomorphisation (like in Rust)!
In languages like C++ or Rust you even, usually, have a choice between monomorphisation and type erasure, these are two fundamentally different ways of doing type-generic programming.
Rust is a bit of an outlier here, most languages that try to use generics (as opposition to templates) then use some kid of type erasure and polymorphic code (be it Extended Pascal, Ada, Java (much discussed here), or Swift) while Rust combines problem of generics with templates (in particular generics in Rust couldn't be used with dynamic linking which is, usually, the #1 motivation for type erasure: you couldn't pass information about type to your generic code when said type doesn't even exist when your code is compiled which, naturally, leads to “pointers + vtable” design and thus type erasure).
Heck, even good old C qsort that exists for half-century or so may be treated as [primitive and manual, yet still valid] kind of type erasure.
Rust (and many other language) are just elevating this technique to the language level, that's all.