Skip to content

Latest commit

 

History

History
110 lines (84 loc) · 2.86 KB

5.md

File metadata and controls

110 lines (84 loc) · 2.86 KB

Generics and Traits

Generics

generics in rusts allow us to have stand-in types for our concrete types. Generics allows us to create code that can operate on many different types. They are abstract stand-ins for concrete types. generics are similar to Generic Interfaces in typescript

#[derive(Debug)]
struct Point<T, U> {
    x: T,
    y: U,
}

let point_int = Point::<i32, i32> { x: 5, y: 8 };
let point_str = Point::<i32, &str> { x: 10, y: "john" };

Traits

traits allows us to represent a capability that can be implemented in many different types. traits represents a capability of what a type can do and can be shared with other types. we can use it to create shared behavior in an abstract way. traits are similar to abi in solidity, interfaces in typescript, classes in dart.

trait Overview {
    fn overview(&self) -> String {
        String::from("this is me learning rust")
    }
}

impl Overview for Course {
    fn overview(&self) -> String {
        format!("{} {}", self.author, self.title)
    }
}

let course1 = Course {
        title: String::from("rust"),
        author: String::from("peter anyaogu"),
    };

println!("{}", course1.overview());

traits can also be passed into functions, as parameters. you would be able to call specific methods implemented in the trait.

fn call_with_trait<T: Overview>(item: &T) {
    println!("overview called: {}", item.overview());
}

call_with_trait(course1);

Drop

simply put, drop frees the memory the associated with variable using it. example when a variable goes out of scope, or moving elements in a vector etc. rust handle this internally. but it can be done manually. eg.

impl Drop for Course {
    fn drop(&mut self) {
        println!("dropping: {}", self.author);
    }
}

drop(course1);
//dropping: peter anyaogu
// without calling drop, the text will still be logged, as it is dropped internally

Clone

clone traits are for types that can make copies of themselves. for it to work, it needs to construct an independent copy using the self keyword and return it. It simply means allocating copies of anything owned. and clones are expensive to run as well

trait Clone:Sized {
    fn clone(&self) -> self;
    fn clone_from(&mut self, source: &self){
        *self = source.clone()
    }
}

Operator Overloading

operator overloading allows us to make our own types support arithmetic

use std::ops::Add;

impl<T, U> Add for Point<T, U>
where
    T: Add<Output = T>,
    U: Add<Output = U>,
{
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Point {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

let coord = Point { x: 3.0, y: 5.0 };
let coord2 = Point { x: 1.0, y: 2.0 };

let sum = coord + coord2;
println!("sum of coordinates are: {:?}", sum);
// sum of coordinates are: Point { x: 4.0, y: 7.0 }