Thursday, March 21, 2024

My first program using Rust - delving into trait...


चरैवेति, चरैवेति - Charaiveti, Charaiveti - keep walking...

because the motion is the life - we need to move on to keep the balance of life...


We must not stop.


The movement is essential.


Life is like a bicycle


The moment it stops - the balance goes for a toss


Life is like a stream.


The moment the flow of the stream is lost, all sorts of fungi pollute the water - and the water becomes dirty and ceases to be a potable one.


So, we have to keep moving all throughout our lives - physically - spiritually - and metaphorically - we must not stop the movement - because the


Ultimate Stop comes with Death.


So, here we go...


After C++, Java and Python...


my first program in Rust delving into trait - which is like an interface in Java or an abstract class of C++...


The cornerstone of abstraction in Rust is traits:

  • Traits are Rust's sole notion of interface. A trait can be implemented by multiple types, and in fact new traits can provide implementations for existing types. 

  • Traits can be statically dispatched. Like C++ templates, you can have the compiler generate a separate copy of an abstraction for each way it is instantiated. Static dispatch generally results in faster code execution because there is no overhead associated with determining which function to call at runtime. The trade-off is that the code footprint will be larger.

Here is an example of static dispatch.

use std::any::type_name;

trait Shape {
fn area(&self) -> f64;
}

struct Circle {
radius: f64,
}

struct Square {
side: f64,
}

impl Shape for Circle {
fn area(&self) -> f64 {
3.14 * self.radius * self.radius
}
}

impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}

fn print_area<T: Shape>(shape: T) {
println!("Area of : {} is {} :", type_name::<T>(), shape.area());
}

fn main() {
let c = Circle { radius: 3.0 };
let s = Square { side: 2.0 };

print_area(c); // Statically dispatched to Circle's implementation of `area`
print_area(s); // Statically dispatched to Square's implementation of `area`
}

  • Traits can be dynamically dispatched. Sometimes you really do need an indirection, and so it doesn't make sense to "erase" an abstraction at runtime. The same notion of interface -- the trait -- can also be used when you want to dispatch at runtime.

Source Code of dynamic dispatch:


trait Animal {
fn make_sound(&self);
fn wag_tail(&self){

println!("i don't have a tail...");
}
}

struct Human{}

impl Animal for Human {
fn make_sound(&self) {
println!("Human is speaking...");
}
}

struct Dog {}

impl Animal for Dog {

fn make_sound(&self) {
println!("Dog barks...");
}

fn wag_tail(&self) {
println!("The dog is waging it's tail");
}
}

fn main() {
let dog = Box::new (Dog {});

dog.make_sound();
dog.wag_tail();

let man = Box::new (Human {});
man.make_sound();
man.wag_tail();

}


The output:


Dog barks... The dog is waging it's tail Human is speaking... i don't have a tail...

No comments: