8

Generics : How to use them and Implement them in Rust

 3 years ago
source link: https://blog.knoldus.com/generics-how-to-use-them-and-implement-them-in-rust/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Reading Time: 4 minutes

When we want to write code for multiple contexts we use Generics. It give us primitives to declare placeholder that allow us to focus less on the specific types. It allows us writing a more concise and clean code by reducing code-duplication and providing type-safety

Generic Functions

In a generic definition, we write the type parameter between open and close angle brackets after the name. In Rust, generic also describes anything that accepts one or more generic type parameters <T>T represents any data type.

Syntax :

pub fn function_name<T>(param:T)

T has been specified as a generic type parameter using <T>. It is considered generic when make param of type T. The parameter can be of any type.

Example :

fn main() {
    let sum_int_value = addition_of_values(3,5);
    println!("Addition of Integer values : {:?}",sum_int_value);
    let sum_float_value = addition_of_values(3.1,5.5);
    print!("Addition of Float values : {:?}",sum_float_value);
}

pub fn addition_of_values<T: PartialOrd + std::ops::Add<Output = T>>(num1: T, num2: T) -> T {
    num1 + num2
}

Output :

sM_PehCjDHd7zPPMf81CFBn2kz4qA2wEv-u3MyPk1IxuRjCAvhUlmbF9xG8AZZG5uC2078rZVII_svKbZKE2Sf4NkSqfMTRQj6chffCqUImbUv0xhQ7a009c1cH2ELNaSuzEpHCh

Generics Traits

Traits can also be generic. Traits can contain methods or abstract methods. Trait definitions are a way to group method signatures to define a set of behaviours necessary to accomplish some purpose.

Syntax :

trait some_trait {
   //abstract method
   fn method1(&self);
   
   fn method2(&self){
     //contents of method
   }
}

Example :

pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

The generic type specified as the type of the item parameters constrains the function.

Generic Struct

To define a generic struct, we write the type parameter after the struct name, between open and close angle brackets. To define a struct, we enter the keyword struct and name the entire struct.

Syntax :

struct STRUCT_NAME<T> {
    field:T,
    field:T,
}

Then, inside curly brackets, we define the names and types of the pieces of data, which we call fields.

Example :

fn main() {
    let int_point = Point {
        num1:4,
        num2:5
    };
    println!("Integer points : {:?}",int_point);
    
    let float_point = Point {
        num1:4.5,
        num2:5.5
    };
    println!("Floating points : {:?}",float_point);
}
#[derive(Debug)]
struct Point<T> {
    num1: T,
    num2: T,
}

Output :

uvavZb00sOBKBxW1e4gLOxRRuOmHeHiYNCIObRErQ-2-5icoGMr0uG2dmpK_DeS6o_zDPCM-Eq18xnUCuxR41JZFUEaqNxl0On1OUvNQDxJnUpBdIMGl9tG9SgBHV8fJdUU1SoLk

Generic Enum

Enums allow you to define a type by enumerating its possible variants. Rust provides us two Enums by default :

  • Option<T>
  • Result<T, E>

jGtbMgaPnenxTdKiOa7O9SQ2g7BgKW5NfQWy-EpBZEF8OlhdtyEvWrZTN4kVmHqFRQUVJ5xDg3tSgAu0VB4QYFSurc2mcfAEHyo75ln7N-iFLzXtlMMr7LZbTlRwGz3XPSPYIdCO

Option<T>

Option enum represents an optional value. Above all, every option has Some and contains a value, or None and does not contain a value.

Syntax :

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

Example :

fn main() {
    let result = find_max_value(5,0);
    match result{
        Some(_) => println!("Num1 is maximum"),
        None => println!("Num2 is maximum")
    }
}

fn find_max_value<T: PartialOrd + std::ops::Rem<Output = T>>(num1:T,num2:T) -> Option<T> {
    match num1 > num2 {
        true => Some(num1),
        false => None
    }
}

Output :

LZV0dRPlFgaLNDdSNpASMUod1LXPAyCcOjYXTCN-uTYfsB1zqctqDT8liQcb3fcfIksR274nnrvpPNPqq4hQr0a0GPvL8iyZ9DHQM1UQlnWnBU2hEp3BskBF6hx5p5jy6iA2LjZV

Result<T,E>

Result enum represents either success (Ok) or failure (Err). Sometimes it is important to express why an operation failed. In that case we must use Result enum.

Syntax :

enum Result<T,E> {
    Ok(T),
    Err(E),
}
  • Ok(value): Indicates that the operation succeeded. Value is of type T.
  • Err(why): Indicates that the operation failed. Why is of type E.

Example :

fn main() {
    let result = find_max_value(5,5);
    match result{
        Ok(value) => println!("Maximum value is : {}",value),
        Err(error) => println!("Error Message : {}",error)
    }
}

fn find_max_value(num1:i32,num2:i32) -> Result<i32,String> {
    match num1 > num2 {
        true => Ok(num1),
        false => Err("Equal numbers".to_string())
    }
}

Output :

vvktqCEP6LLqLMof9jjexV0w53rQDwHElMtH5G-HImqTW5r3nO0Edwh6-_9xztLEMyhRZYkikeRSr1R5eocqLzSSbD9xSpd6SuapWbxm8C0MDUG3wGFxbtOTORr-pLgYMnugwMDn

So hopefully you get an idea of how generics are being used in Rust.

Thanks for reading !!

If you want to read more content like this?  Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter: https://bit.ly/2Vdlld7.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK