Pattern Type State

Chester Wyke October 12, 2023 Updated: April 15, 2025 #rust

Resource List

Annotated example

use std::collections::HashMap;
use std::marker::PhantomData;

struct Locked;
struct Unlocked;

// Note that PasswordManager<Locked> != PasswordManager<Unlocked> because of the state field with doesn't use space at runtime only a compile time check

// Locked is the default state here
struct PasswordManager<State = Locked> {
    master_pass: String,
    passwords: HashMap<String, String>,
    state: PhantomData<State>, // This doesn't take up space at runtime
}

// impl only available while "Locked"
impl PasswordManager<Locked> {
    pub fn unlock(self, master_pass: String) -> PasswordManager<Unlocked> {
        PasswordManager {
            master_pass: self.master_pass,
            passwords: self.passwords,
            state: PhantomData,
        }
    }
}

// impl only available while "Unlocked"
impl PasswordManager<Unlocked> {
    pub fn lock(self) -> PasswordManager<Locked> {
        PasswordManager {
            master_pass: self.master_pass,
            passwords: self.passwords,
            state: PhantomData,
        }
    }

    pub fn list_passwords(&self) -> &HashMap<String, String> {
        &self.passwords
    }

    pub fn add_password(&mut self, username: String, password: String) {
        self.passwords.insert(username, password);
    }
}

// impl available in any State (Use T to guard against typos)
impl<T> PasswordManager<T> {
    pub fn encryption(&self) -> String {
        todo!()
    }

    pub fn version(&self) -> String {
        todo!()
    }
}

// impl available before a state is set (Only true static function)
impl PasswordManager {
    pub fn new(master_pass: String) -> Self {
        PasswordManager {
            master_pass,
            passwords: Default::default(),
            state: PhantomData,
        }
    }
}

fn main() {
    let mut manager = PasswordManager::new("password123".to_owned());
    let manager = manager.unlock("password123".to_owned());
    manager.list_passwords();
    manager.lock();
}