Sunday, August 25, 2024

From Object Oriented Analysis and Design to Language like Rust - a major paradigm shift in the programming world...


चरैवेति, चरैवेति - 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.

So here we go... moving forward from C++/Java/OOAD to Rust...

In Rust... i keep my Trust...

The first language that I learned in the software world was C++ - that was back in mid '90s. From that time onwards, almost 30 years have passed by. During this time the software world was dominated by C++, Java and other OOAD stuffs.

I experienced the use cases of Unified Modelling Language and it's usefulness in the design process of an object oriented software project.

From C to C++ and then Java, it was the first major paradigm shift.

And now, it seems we are going to experience another paradigm shift in the world of software.

With language like Rust picking up the popularity, will demand from the programmers to shift the gear - from maybe C++ or Java to Rust.

Rust is neither a pure object oriented like Java nor an absolute functional language. Rather it has taken the good stuffs from both Object oriented domain and functional domain to offer an unique experience to the programmers.

It's definitely a major paradigm shift in the world of computer programming.

Let us try to understand some features and you will feel the differences.

The first major difference is that Rust has avoided the class based inheritance model of standard OOAD domain. In the OOAD domain we encapsulate the state and the behaviour together in a class and we extend that class, which is known as inheritance to offer versatility in the behaviours.

In Rust, we define Traits and use Traits (which are similar to Interface in the OOAD world) and composition rather than inheritance. This approach encourages composition over inheritance, meaning that instead of creating deep hierarchies, you build functionality by combining smaller, focused pieces.

The next obvious difference is the way we handle memory related issues in C++ and Java. In Java, we have garbage collection and in modern C++, we handle it through Boost's library and pointers.

However, Rust uses its unique ownership model. Each value in Rust has a single owner, and memory is automatically reclaimed when the owner goes out of scope. Additionally, Rust uses borrowing and lifetimes to ensure that references to data do not outlive the data itself, preventing many common errors like null pointer dereferences or data races.

The other paradigm shift is the way Rust handles polymorphism. Polymorphism in standard OOAD is done using inheritance. The shift in Rust involves moving from a default of dynamic dispatch and inheritance to a system that favours static dispatch (monomorphization) with the option for dynamic dispatch when necessary. This results in more performant code but requires more deliberate design decisions about when to use dynamic behaviour. Static dispatch may increase the code foot print but it will be much faster than dynamic dispatch and concepts like VTble.

There are also differences between a standard OOAD exception handling and the way Rust handles it using Result and Option.

I have heard that the way Rust manages concurrency is also kind of a paradigm shift in the thought processing of an OOAD engineer. However, I am yet to cover all those things and hence no comment. for the time being.

Now let us try to understand some of the nitty gritties of Rust vis-a-vis  UML as this was the back bone in the world of OOAD design.

You will be surprised to know that we can't get similar effects of Aggregation in Rust because of it's lifetime management. We can simulate the Agrregation in Rust. Nevertheless it won't be exact aggregation.

Look at the following piece of code.

struct Engine {
    horsepower: u32,
}
struct Car<'a> {
    engine: &'a Engine,  // Aggregation-like relationship
}
fn main() {
    let engine = Engine { horsepower: 300 };
    let car = Car { engine: &engine };
    println!("Car has an engine with {} horsepower.",             car.engine.horsepower);
}


Explanation:

&'a Engine: The Car struct contains a reference to an Engine. This setup means that the Engine can exist independently of the Car, similar to how aggregation works in UML.

Ownership and Borrowing: Unlike aggregation in traditional OOPS, Rust's borrowing rules ensure that you cannot accidentally leave a reference dangling (i.e., referencing an object that no longer exists). That means once the car is destroyed, it will take away the engine with it - unlike the traditional OOAD aggregation.

So what is the alternative? Use Ownership - much like Composition in UML. Look at the following piece of code.

struct Engine {
    horsepower: u32,
}

struct Car {
    engine: Engine,  // Composition-like relationship
}

fn main() {
    let engine = Engine { horsepower: 300 };
    let car = Car { engine };  // The Car now owns the engine

    println!("Car has an engine with {} horsepower.", car.engine.horsepower);


This is a Work In Progress.

Will gradually add other aspects of Rust in the near future.

For all the engineers who embraced the Gang of Four design pattern book as their skill set, will have to embrace the paradigm shift in the world of programming.

It's time to embrace changes - and keep this book as a great influencer for the OOAD programmers.



No comments: