What is enum

Enums are User defined data type(Similar to other languages) which can be assigned some limited values. In rust enum variable can/cannot hold the data (Eg: String).
C++ enums are named integer constants, whereas Rust enums are Data Types that can carry distinct payloads of data per variant

Properties of enum in Rust

Property Description
Individual variants can contain completely different types of data

// Rust
enum WebEvent {
    PageLoad,                 // No data
    KeyPress(char),           // Tuple variant
    Click { x: i64, y: i64 }, // Anonymous struct variant
}
      
Attach behavior directly to your enums using impl blocks

impl WebEvent {
    fn log_event(&self) {
        // Method logic here
    }
}
                
Safety via Exhaustive Pattern Matching Rust forces you to account for every single enum variant using the match keyword. Leaving out a variant causes a compile-time failure, preventing unintended runtime edge cases.
wrt C++. While a modern C++ compiler can generate warnings for unhandled cases inside a switch statement on an enum class, it will still compile and run.
Specialized optimizations Because the Rust compiler understands the exact layout of its enums, it performs specialized optimizations.

enum types

1. Not holding data


#[derive(Debug)]            //To print debugging information
enum Color{                 //Declare enum
    Red,
    Blue,
    Green
}

fn fun (a:Color) {          //Print enum after passing to function
    println!("{:#?}",a);
}

fn main() {
    let a:Color = Color::Red; //Initialize variable with enum value
    fun(a);
}
            

2. Enum Holding the data


// Enum holding 1 datatype
use std::{string::String, u32};

#[derive(Debug)]
enum Address{                     //1. Enum variable storing string data
    IPv4(String),
    IPv6(String),
}

fn fun (addr:&Address) {          //3. Passing enum variable to function and print
    println!("{:#?}",addr);
}

fn main() {
    let router1:Address = 
      Address::IPv4(String::from("192.168.1.1"));   //2. Creating enum variable having string data(192.168.1.1)
    fun(&router1);
}

# rustc test.rs
# ./test.exe
IPv4(
    "192.168.1.1",
)

// Enum Holding Multiple Datatypes
use std::{string::String, u32};

#[derive(Debug)]                
enum Address{                   //1. Enum data structure holding 3 variables
    NoIP,                       //NoIP does not hold any data
    IPv4(u8,u8,u8,u8),          //IPv4 holding 4 u8 types
    IPv6(String),               //IPv6 holding String type
}

/*THIS IS EQUIVALENT TO:
struct NoIP;
struct IPv4 {
    a: i32,
    b: i32,
    c: i32,
    d: i32,
}
struct IPv6 {
    e:String
}    
*/

fn fun (addr1:&Address,addr2:Address, addr3:Address) {         //3. Printing enum variables
    println!("IPv4 {:#?}",addr1);
    println!("IPv6 {:#?}",addr2);
    println!("Disconnected {:#?}",addr3);
}

fn main() {
    let router1:Address = 
      Address::IPv4(10,11,12,13);                         //2. Router1 variable type=IPv4
    let router2:Address = Address::IPv6(String::from("::1"));   //router2 of IPv6
    let disconnectedDevice:Address = Address::NoIP;             //disconnectedIP of no ip
    fun(&router1,router2);
}

# rustc test.rs
# .\test.exe
IPv4 IPv4(
    10,
    11,
    12,
    13,
)
IPv6 IPv6(
    "::1",
)
Disconnected NoIP
            

Option Enum = NULL

The Option enum in Rust is a built-in generic type used to represent a value that may or may not be present.
Because Rust does not have a null value like many other programming languages, it relies on the Option type to safely encode the concept of "nothingness" without risking null pointer

Usage of Option Enum: Preventing Null Pointer Exceptions: In other languages, forgetting to check for null can crash your application at runtime. Rust forces you to acknowledge the None variant via the compiler, guaranteeing safety.

Syntax

pub enum Option<T> {
    Some(T), // Contains a value of type T
    None,    // Represents the absence of a value
}
      

fn divide(numerator: f64, denominator: f64) ->>Option<f64> {
    if denominator == 0.0 {
        None // Division by zero is impossible, return "nothing"
    } else {
        Some(numerator / denominator) // Return the calculated value wrapped in Some
    }
}
fn main() {
    let result1 = divide(10.0, 2.0);
    let result2 = divide(10.0, 0.0);

    // Handling result1 using a match statement
    match result1 {
        Some(value) => println!("Success! The result is: {}", value),
        None > println!("Error: Cannot divide by zero!"),
    }
}
            

enum Option <T> {        //T is template which can take any type: i32, i64 etc
    Some (T),
    None,
}

1. Initializing and using Option <T>
fn main() {
    let a : Option <i32> = Some(2);
    let b : Option <i32> = None;

    println! ("a:{:#?}",a);
    println! ("b:{:#?}",b);
}    
$ ./main.exe
a : Some(
    2,
)     
b : None

2. Option <T> cannot be added to non-Option <T>
fn main() {
    let x: i8 = 5;
    let y: Option <i8> = Some(5);

    let sum = x + y;                //Compliation Error
}    
            

Creating NULL was mistake?

Tony Hoare(inventor of NULL) said invention of NULL is his Billion dollar mistake. Why?
Using NULL value as non-NULL value. Eg: derefercing NULL ptr. It will crash. Problem is not in NULL but at places it gets misused.
There are multiple situations, where crash is caused due to NULL. Example: NULL References when derefrenced will cause crash


int fun(struct A*& p){              //Funtion taking NULL reference
    if (p == nullptr)
        cout << "Got Null ptr reference\n";
    cout << p->a;
}
int main(){
    struct A* p = nullptr;
    fun(p);
}
$ ./a.out
Got Null ptr reference
Segmentation Fault
            

How Rust avoid Null ptr?

1. Variable that can have NULL/other value is declared as Option <T>
2. Rust does not allow creating structure object without initializing its members. It can be initialized with Some or None. No crash, None will be printed.