Generics are used to reduce code duplication
It allows to creates functions that can work on multiple datatypes
Generics in Function
fn largest_element<T: PartialOrd>(vector: &Vec<T>) -> &T {
let mut largest = &vector[0];
for element in vector {
if element > largest {
largest = element;
}
}
largest
}
PartialOrd
is an trait that is implemented by elements that can be compared
Generics in Structs & Enums
struct Point<T, U> {
x: T,
y: U,
}
let point1 = Point { x: 2, y: 4 };
let point2 = Point { x: 2.0, y: 4.0 };
let point3 = Point { x: 2, y: 4.0 };
Here T and U can be of the same datatype as well
enum Some<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
Generics on Methods
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
impl Point<f64> {
fn y(&self) -> f64 {
self.y
}
}
The x method will be available for all datatypes but y will only be present on f64
datatype
The generic type mentioned in the implementation block can use a different variable (it will not make any different)
struct Point<X1, Y1> {
x: X1,
y: Y1,
}
impl<X1, Y1> Point<X1, Y1> {
fn mix_up<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
Point {
x: self.x,
y: other.y,
}
}
}
let point3 = point1.mix_up(point2);
println!("{:?}", point3);
The types of the point that is passed to the method is different from the type of the point on which the method is defined
Performance
Using generics we do not incur any performance penalty as on compile time discrete types of the generics are created as required
enum Option_i32 {
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
}