No Crash in Rust due to: 1. Ownership, 2. Borrowing, 3. No Manual Memory management
Faster wrt C++: 4. Aggressive Complier Optimizations, 5. Lower Runtime overheads wrt C++
Smaller Binary wrt C++: 6. No RTTI in Rust

Rust Over C++

C++ Rust
Ownership No concept of ownership Yes
Reference(or Borrowing) Unsafe (Lead to crashes) Safe (No crash)

1. Ownership (avoid crash in Rust)

Ownership?
- Defines how memory is managed safely(without need of garbage collector) by Rust program.
- Every Variable/data have single owner at a time. When the owner goes out of scope, Rust automatically deallocates the memory.
- Ownership rules are checked at compile time. Ownership applies to memory allocated on heap(dynamically), Eg: String(), vector ie those are moved. For variable allocated on Stack (Eg: int, float) variable is copied.

In Rust, raw pointers (*const T and *mut T) don't embody ownership semantics directly. However, smart pointers like Box, Rc, and Arc do.
- Box: It represents ownership of a heap-allocated value.
- Rc: It provides reference counting and allows multiple ownership.
- Arc: It's like Rc, but with atomic reference counting, suitable for concurrent access.
C++ (Crash in C++) Rust (No null pointer Exception(Crash) in Rust)

#include <iostream>
#include <memory>  // For std::unique_ptr
using namespace std;

int main() {
    // Create a unique_ptr to manage the std::string object
    unique_ptr <string> s = make_unique("hello");

    // Transfer ownership to s1
    unique_ptr < string> s1 = move(s);

    // No compile time check. Run time Segmentation fault
    cout << *s;

    std::cout << *s1;

    return 0;
}
                        

fn main() {
    // Box owns the heap-allocated String
    let s = Box::new(String::from("hello")); 

    // Ownership transferred to s1
    let s1 = s; 

    // Compilation error
    // value borrowed here after move
    // The ownership of the String was moved to s1, 
    //so s is no longer valid to use
    println!("{}", s);

    // The String is still valid through s1
    println!("{}", s1);
} // Box goes out of scope, 
// and the String is deallocated automatically
                            
                        

3. No Manual Memory management (avoid crash in Rust)

- Rust does not provide `new, delete` operators as in C++ for dynamically allocate memory.
- Though C++ provides smart pointers, still someone can leak using C++98 new() and delete()
- Rust's ownership and borrowing rules ensure memory safety without the need for manual memory management.
C++

int* ptr = new int[10]; // Allocates memory of 10 integer
delete ptr; // Deallocates the memory pointed to by ptr
                    
Rust.

let mut arr: Vec = Vec::with_capacity(10);     //malloc
let arr: Vec = vec![0; 10];                    //malloc + memset
                    

4. Aggressive Compiler Optimization (make Rust fast)

1. Inlining

- Rust's Compiler(LLVM) optimizes code more aggressivly wrt C++ compiler & this is because of ownership rules, LLVM can make assumptions.
- LLVM is more aggressive about inlining functions, especially for small functions. Inlining avoids function call overhead and makes it fast.

5. Lower Runtime overheads wrt C++ (make Rust fast)

5.1 C++ Stack Unwinding causes runtime slowness

C++ (On Exception Stack Unwinding happens)

#include <iostream>
#include <stdexcept>

int divide(int a, int b) {
    if (b == 0) {
        throw std::runtime_error("Division by zero");
    }
    return a / b;
}
int main() {
    try {
        int result = divide(10, 0);
        std::cout << "Result: " << result << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}        
                    
Rust (Result and Option types for error handling,
which are handled through pattern matching rather than exceptions)

fn divide(a: i32, b: i32) -> Result {
    if b == 0 {
        return Err("Division by zero");
    }
    Ok(a / b)
}

fn main() {
    match divide(10, 0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => eprintln!("Error: {}", e),
    }
}                        
                    

5.2 C++ Runtime Type Information (RTTI) increases binary size and runtime overhead

Rust's type system(based on traits and enums), enables polymorphic behavior and dynamic dispatch without the need for RTTI.
C++

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {};

int main() {
    Base* ptr = new Derived();
    Derived* derived = dynamic_cast(ptr);
    if (derived) {
        std::cout << "Dynamic cast successful\n";
    } else {
        std::cout << "Dynamic cast failed\n";
    }
    delete ptr;
    return 0;
}
                        
Rust

Box enable dynamic dispatch without the need for runtime type information.

trait Printable {
    fn print(&self);
}

struct Base;
struct Derived;

impl Printable for Base {
    fn print(&self) {
        println!("Base");
    }
}

impl Printable for Derived {
    fn print(&self) {
        println!("Derived");
    }
}

fn main() {
    let base: Box = Box::new(Derived);
    base.print(); // Dynamic dispatch without RTTI
}