Industrial Training




Rust Generics

When we want to create the function of multiple forms, i.e., the parameters of the function can accept the multiple types of data. This can be achieved through generics. Generics are also known as 'parametric polymorphism' where poly is multiple, and morph is form.


There are two ways to provide the generic code:
    Option< T> Result< T, E>


1. Option< T>: Rust standard library provides Option where 'T' is the generic data type. It provides the generic over one type.


 enum Option< T>  
{  
    Some(T),  
    None,  
}  

In the above case, enum is the custom type where is the generic data type. We can substitute the 'T' with any data type. Let's look at this:

let x : Option< i32> = Some(10);  // 'T' is of type i32.  
let x : Option< bool> = Some(true);  // 'T' is of type bool.  
let x : Option< f64> = Some(10.5); // 'T' is of type f64.  
let x : Option< char> = Some('b'); // 'T' is of type char.   

In the above case, we observe that 'T' can be of any type, i.e., i32, bool, f64 or char. But, if the type on the left-hand side and the value on the right hand side didn't match, then the error occurs. Let's look at this:


let x : Option< i32> = Some(10.8);   

In the above case, type on the left-hand side is i32, and the value on the right-hand side is of type f64. Therefore, the error occurs "type mismatched".


2. Result< T,E>: Rust standard library provides another data type Result< T,E> which is generic over two type, i.e., T &E:


  enum Result  
   {  
      OK(T),  
        Err(E),  
}  

Note: It is not a convention that we have to use 'T' and 'E'. We can use any capital letter.


Generic functions


Generics can be used in the functions, and we place the generics in the signature of the function, where the data type of the parameters and the return value is specified.


    When the function contains a single argument of type 'T'.

Syntax:


fn function_name< T>(x:T)   
  
  // body of the function.  

The above syntax has two parts:
    < T> : The given function is a generic over one type.
    (x : T) : x is of type T.

When the function contains multiple arguments of the same type.
fn function_name(x:T, y:T)   
  
  // body of the function.  

When the function contains arguments of multiple types.
fn function_name(x:T, y:U)  
  
    // Body of the function.  


 fn main()  
{  
  let a = vec![1,2,3,4,5];  
  let b = vec![2.3,3.3,4.3,5.3];  
  let result = add(&a);  
  let result1 = add(&b);  
  println!("The value of result is {}",result);  
  println!("The value of result1 is {}",result1);  
}  
  
fn add(list:&[T])->T  
{  
  let mut c =0;  
  for &item in list.iter()  
  {  
    c= c+item;  
  }  
  c}  

Struct Definitions


Structs can also use the generic type parameter in one or more fields using <> operator.


Syntax:
struct structure_name< T>   
  
  // Body of the structure.  

In the above syntax, we declare the generic type parameter within the angular brackets just after the structure_name, and then we can use the generic type inside the struct definition.


Let's see a simple example:
struct Value  
{  
  a:T,  
  b:T,  
}  
fn main()  
{  
  let integer = Value{a:2,b:3};  
  let float = Value{a:7.8,b:12.3};  
  println!("integer values : {},{}",integer.a,integer.b);  
  println!("Float values :{},{}",float.a,float.b);  
}  

Output:
integer values : 2,3
Float values : 7.8,12.3

In the above example, Value< T> struct is generic over one type and a and b are of the same type. We create two instances integer and float. Integer contains the values of type i32 and float contains the values of type f64.


Let's see another simple example.
struct Value< T>  
{  
  a:T,  
  b:T,  
}  
fn main()  
{  
  let c = Value{a:2,b:3.6};  
  println!("c values : {},{}",c.a,c.b);  
 }  

Output:

In the above example, Value< T> struct is generic over one type, and a and b are of the same type. We create an instance of 'c'. The 'c' contains the value of different types, i.e., i32 and f64. Therefore, the Rust compiler throws the "mismatched error".


Enum Definitions


An enum can also use the generic data types.Rust standard library provides the Option< T> enum which holds the generic data type. The Option< T> is an enum where 'T' is a generic data type.


    Option< T>

It consists of two variants, i.e., Some(T) and None.



Where Some(T) holds the value of type T and None does not contain any value.


Let's look:
 enum Option< T>  
{  
    Some(T),  
    None,  
}  

In the above case, Option is an enum which is generic over one type 'T'. It consists of two variants Some(T) and None.


    Result< T, E>: We can create the generic of multiple types. This can be achieved through Result< T, E>.

 enum Result< T,E>  
{  
    OK(T),  
    Err(E),  
}   

In the above case, Result< T, E> is an enum which is generic over two types, and it consists of two variants, i.e., OK(T) and Err(E).
OK(T) holds the value of type 'T' while Err(E) holds the value of type 'E'.


Method Definitions


We can implement the methods on structs and enums.


Let's see a simple example:
 struct Program< T> {  
    a: T,  
    b: T,  
}  
impl< T> Program< T>   
{  
    fn a(&self) -> &T   
{  
       &self.a  
    }  
}  
fn main() {  
let p = Program{ a: 5, b: 10 };  
  
    println!("p.a() is {}", p.a());  
}  

Output:
p.a() is 5

In the above example, we have implemented the method named as 'a' on the Program< T> that returns a reference to the data present in the variable a.
We have declared the 'T' after impl to specify that we are implementing the method on Program< T>.


Resolving Ambiquities


Rust compiler automatically infers the generic parameters. Let's understand this through a simple scenario:


Let mut v = Vec::new();   // creating a vector.  
v.push(10); // inserts integer value into the vector. Therefore, v is of i32 type.  
println!("{:?}", v); // prints the value of v.  

In the above case, we insert the integer value into the vector. Therefore, the Rust compiler got to know that the vector v has the type i32.
If we delete the second last line, then it looks like;


Let mut v = Vec::new();   // creating a vector.  
println!("{:?}", v); // prints the value of v.  

The above case will throw an error that "it cannot infer the type for T".


    We can solve the above case in two ways:

1. We can use the following annotation:


let v : Vec< bool> = Vec::new();  
println!("{:?}",v) ;  

2. We can bind the generic parameter 'T' by using the 'turbofish' ::<> operator:


let v = Vec :: < bool> :: new();  
println!("{:?}",v) ;  



Hi I am Pluto.