What Are Generics?
- Generics are a way of generalizing types; they define the data type at run time. Generics are called parametric polymorphism in type theory. ‘Poly’ is multiple, ‘morph’ is form over a given parameter (‘parametric’) meaning multiple forms of a given parameter.
-
They can be applied to methods, functions, structures, enumerations, collections, and traits. This helps to reuse the same code but with a different type.
- Syntax
The <T>
is known as the type parameter and is used to declare a generic construct. T can be any data-type.
- Example 1: Generic Function
The following example defines a generic function that displays the value passed to it as a parameter. The first value type is a string and the second value is an integer of type i32
.
- Note: For printing a value of the passed parameter, write use
std::fmt::Display
prior to function definition and after the generic function’s name write<T:Display>
following the function name prior to writing the passing parameters.
Note: Display
is a trait
fn main(){
println!("- Passing a string literal");
concatenate(" Rust ", " Programming ");
println!("- Passing an integer");
concatenate(10 as i32, 1 as i32);
}
use std::fmt::Display;
fn concatenate<T:Display>(t:T, s:T){
let result = format!("{}{}", t , s);
println!("{}", result);
}
output
- Passing a string literal
Rust Programming
- Passing an integer
101
Example 2: Generic Vector
The following example creates a vector my_int_vector
of type i32
:
fn main(){
let mut my_int_vector: Vec<i32> = vec![1,2];
my_int_vector.push(3);
println!("{:?}",my_int_vector);
// my_int_vector.push("Rust"); // mismatched types error
}
output
[1, 2, 3]
Note : while making a vector in the same function, we cannot actually do this. However, a vector of type T can be passed to the function.
use std::fmt::Display;
fn print_vec<T:Display>(v: &[T]) {
for i in v.iter() {
print!("{}", i)
}
println!("");
}
fn main() {
let int_vec = [1, 2, 3, 4, 5]; // define a vector of type integer
println!("Call to the function with vector of integers");
print_vec(& int_vec); // pass vector of type integer to the function
println!("Call to the function with vector of strings");
let str_vec = ["Rust", "Programming"]; // define a vector of type string
print_vec(&str_vec); // pass vector of type String to the function
}
output
Call to the function with vector of integers
12345
Call to the function with vector of strings
RustProgramming
Example 3: Generic Struct
The following example creates a struct Rectangle. The struct gets invoked with type instances of type i32 and f32 respectively:
struct Rectangle<T> {
width:T,
height:T
}
fn main() {
//generic type of i32
let r1:Rectangle<i32> = Rectangle{width:250, height:150};
println!("Width:{}, Height:{}", r1.width, r1.height);
//generic type of String
let r2:Rectangle<f32> = Rectangle{width:240.0, height:250.0};
println!("Width:{}, Height:{}", r2.width, r2.height);
}
output
Width:250, Height:150
Width:240, Height:250
Quiz
Test your understanding of generics in Rust.
- Which of the following types are not allowed to be made generic in Rust!
A) vectors
B) arrays
C) structs
D) enums