Generics
- Generics are used to prevent the duplication of concepts and are generalized over a type.
- Some examples of generics are
Result<T,E>
,Option<T>
,Vec<T>
, andHashMap<K,V>
. - Possible Use Cases:
- You can define an enum or struct which can accomodate different data types.
- You can define a function which can provide same functionality for different types. For Example, finding the largest element inside a vector of numbers or chars.
- In Rust, declaring generics aren't any slower than using concrete types, because it uses a process called Monomorphization to achieve that. Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.
Generics on structs
-
To create a generic struct:
// We used type T to make the struct generic // so that it can accomodate any type struct Point<T> { x: T, y: T, } fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; // FAIL: First is i32 and the other is f32, hence different types. let wont_work = Point { x: 5, y: 4.0 }; }
-
To make the
wont_work
to work fine, we'll need to change the code as follows:struct Point<T, U> { x: T, y: U, } fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; let will_work = Point { x: 5, y: 4.0 }; }
Generics on Enums
-
The
Option<T>
enum:#![allow(unused)] fn main() { enum Option<T> { Some(T), None } }
-
The
Result<T, E>
enum uses two types:#![allow(unused)] fn main() { enum Result<T, E> { Ok(T), Err(E), } }
Generics on Functions
-
To use generics on
impl
blocks:struct Point<T> { x: T, y: T, } // By using T after impl means that impl<T> Point<T> { fn x(&self) -> &T { &self.x } } // impl for just one concrete type impl Point<f32> { fn distance_from_origin(&self) -> f32 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } fn main() { let p = Point { x: 5, y: 10 }; println!("p.x = {}", p.x()); }
-
To use on
impl
on different types:struct Point<X1, Y1> { x: X1, y: Y1, } impl<X1, Y1> Point<X1, Y1> { fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> { Point { x: self.x, y: other.y, } } } fn main() { let p1 = Point { x: 5, y: 10.4 }; let p2 = Point { x: "Hello", y: 'c' }; let p3 = p1.mixup(p2); println!("p3.x = {}, p3.y = {}", p3.x, p3.y); }