PhVHoang / TIL

10 stars 0 forks source link

TypeState #467

Open PhVHoang opened 2 days ago

PhVHoang commented 2 days ago

The typestate pattern is a design pattern used in programming to encode the possible states of an object into the type system, allowing the compiler to enforce state transitions at compile time. This pattern is particularly useful in preventing invalid state transitions and ensuring that operations are only performed when the object is in the correct state.

Core Ideas of the Typestate Pattern

  1. State as Types: Instead of tracking the state of an object through fields or variables, the state is represented by different types. Each type corresponds to a particular state.

  2. State Transitions: Moving between states is done by changing the type of the object. Each transition is represented by a method that consumes the current type and returns a new type.

  3. Compile-Time Guarantees: Since the state is encoded in the type, the compiler can ensure that only valid operations are performed in each state, reducing runtime errors.

Example: File handling with TypeState

struct FileClosed;
struct FileOpen;

struct File<T> {
    name: String,
    state: std::marker::PhantomData<T>,
}

impl File<FileClosed> {
    fn new(name: &str) -> File<FileClosed> {
        File {
            name: name.to_string(),
            state: std::marker::PhantomData,
        }
    }

    fn open(self) -> File<FileOpen> {
        println!("Opening file: {}", self.name);
        File {
            name: self.name,
            state: std::marker::PhantomData,
        }
    }
}

impl File<FileOpen> {
    fn read(&self) {
        println!("Reading from file: {}", self.name);
    }

    fn close(self) -> File<FileClosed> {
        println!("Closing file: {}", self.name);
        File {
            name: self.name,
            state: std::marker::PhantomData,
        }
    }
}

fn main() {
    let file = File::new("example.txt");
    let open_file = file.open();
    open_file.read();
    let _closed_file = open_file.close();
}