Is there type erasure of generics in Rust (like in Java) or not? I am unable to find a definitive answer.
When you use a generic function or a generic type, the compiler generates a separate instance for each distinct set of type parameters (I believe lifetime parameters are ignored, as they have no influence on the generated code). This process is called monomorphization. For instance, Vec<i32>
and Vec<String>
are different types, and therefore Vec<i32>::len()
and Vec<String>::len()
are different functions. This is necessary, because Vec<i32>
and Vec<String>
have different memory layouts, and thus need different machine code! Therefore, no, there is no type erasure.
If we use Any::type_id()
, as in the following example:
use std::any::Any;
fn main() {
let v1: Vec<i32> = Vec::new();
let v2: Vec<String> = Vec::new();
let a1 = &v1 as &dyn Any;
let a2 = &v2 as &dyn Any;
println!("{:?}", a1.type_id());
println!("{:?}", a2.type_id());
}
we obtain different type IDs for two instances of Vec
. This supports the fact that Vec<i32>
and Vec<String>
are distinct types.
However, reflection capabilities in Rust are limited; Any
is pretty much all we've got for now. You cannot obtain more information about the type of a runtime value, such as its name or its members. In order to be able to work with Any
, you must cast it (using Any::downcast_ref()
or Any::downcast_mut()
to a type that is known at compile time.
String
in an ArrayList<Integer>
, because there is no such thing as an ArrayList<Integer>
at runtime; it's just an ArrayList
. The only advantage to using generics in Java is that the compiler will insert casts for you where it's safe, whereas without generics, you'd have to type the casts yourself (and you could get them wrong). –
Electrolyze Vec<Box<Any>>
? –
Brilliance Vec<Box<Any>>
would allow you to store objects of any type that implements Any
, but you could not use it as if it was, say, a Vec<i32>
or a Vec<String>
. In Java, type erasure is only possible because type parameters can only be replaced with reference types (you can't use primitive types like int
). Since all reference types have the same size (a pointer), all instantiations of a generic class or method would be identical (bar a few oddities such as new T[]
which doesn't work). –
Electrolyze Rust does have type erasure in the form of virtual method dispatch via dyn Trait
, which allows you to have a Vec
where the elements have different concrete types:
fn main() {
let list: Vec<Box<dyn ToString>> = vec![Box::new(1), Box::new("hello")];
for item in list {
println!("{}", item.to_string());
}
}
Note that the compiler requires you to manually box the elements since it must know the size of every value at compile time. You can use a Box
, which has the same size no matter what it points to since it's just a pointer to the heap. You can also use &
-references:
fn main() {
let list: Vec<&dyn ToString> = vec![&1, &"hello"];
for item in list {
println!("{}", item.to_string());
}
}
However, note that if you use &
-references you may run into lifetime issues.
Vec<&dyn ToString>
is distinguishable from Vec<&dyn SomethingElse>
, and trying to insert something that doesn't have to_string()
into the former will fail. For a Java comparison, its seems rather like reverse-engineering an interface
and using that as the type -- but consistent with any other generic type in Rust (and unlike Java), it is reified. –
Bewitch © 2022 - 2024 — McMap. All rights reserved.