Files
type-level-demo/03-builder-pattern
2026-07-26 23:52:26 +02:00
..
2026-07-16 12:28:44 +02:00
2026-07-15 22:20:36 +02:00
2026-07-26 23:52:26 +02:00

Solution 2: "Builder Pattern"

A better solution is to use two different structs for each state with their separate implementations. That ensured on a compiler level that the manager can not be misused in either state it is.

let pw = LockedCredentialManager::new();
pw.set("foo", "bar");

This produces the following compiler error:

error[E0599]: no method named `set` found for struct `LockedCredentialManager` in the current scope
  --> 03-builder-pattern\src\main.rs:45:8
   |
 5 | struct LockedCredentialManager {
   | ------------------------------ method `set` not found for this struct
...
45 |     pw.set("foo", "bar");
   |        ^^^ method not found in `LockedCredentialManager`

And because we pass the struct by reference in the unlock and lock methods, resulting from Rust's onwership memory model, the previous object can not be used after the transformation. And because this is checked at compile time, it makes the double locking and unlocking a non-issue in production. Let's see what the following code results in.

    let pw = LockedCredentialManager::new();
    let mut pw = pw.unlock("pssst");
    pw.lock();
    pw.lock();

This code results in the following compiler error:

error[E0382]: use of moved value: `pw`
  --> 03-builder-pattern\src\main.rs:49:5
   |
45 |     let mut pw = pw.unlock("pssst");
   |         ------ move occurs because `pw` has type `UnlockedCredentialManager`, which does not implement the `Copy` trait
...
48 |     pw.lock();
   |        ------ `pw` moved due to this method call
49 |     pw.lock();
   |     ^^ value used here after move
   |
note: `UnlockedCredentialManager::lock` takes ownership of the receiver `self`, which moves `pw`
  --> 03-builder-pattern\src\main.rs:36:17
   |
36 |     pub fn lock(self) -> LockedCredentialManager {
   |                 ^^^^