Does Rust erase generic types or not?
Asked Answered
C

2

21

Is there type erasure of generics in Rust (like in Java) or not? I am unable to find a definitive answer.

Citrange answered 13/9, 2015 at 3:36 Comment(3)
You may need to define what you mean by "type erasure" in order to get the most useful responses.Furnishings
@Furnishings Type erasure for generics is a well-defined concept in JVM languages like Java and Scala, but does not occur with generics in any other language I know. People who are proficient in modern Java will know what "type erasure" means.Siusiubhan
This explains it quite well: A light comparison between Rust and Java generics and type system features.Gherlein
B
29

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.

Bulbil answered 13/9, 2015 at 3:46 Comment(4)
Looks like you and I disagree ^_^. Perhaps there is a nuance to the question I am missing...Furnishings
@Shepmaster: In Java, type erasure is relevant because it's as if the type parameters were never there. For example, you can store a 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
What about Vec<Box<Any>>?Brilliance
@aochagavia: I don't consider this type erasure in the same way as in Java. A 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
E
4

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());
    }
}

(playground)

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());
    }
}

(playground)

However, note that if you use &-references you may run into lifetime issues.

Exude answered 18/12, 2020 at 0:22 Comment(1)
This isn't type erasure. 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.