Advertisement
NLinker

Phantom types or Typestate pattern in Rust

Mar 29th, 2024 (edited)
1,539
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 3.29 KB | Software | 0 0
  1. mod without_phantom_types {
  2.     /// struct without phantom types
  3.     /// public_key may be absent, because the generating of the key pair
  4.     /// may happen later, than the creation of the ports
  5.     #[derive(Debug, Clone)]
  6.     struct WireguardPort {
  7.         id: i32,
  8.         public_key: Option<String>,
  9.     }
  10.  
  11.     impl WireguardPort {
  12.         fn make(id: i32) -> Self { Self { id, public_key: None } }
  13.         fn with_public_key(&self, public_key: &str) -> Self {
  14.             WireguardPort { id: self.id, public_key: Some(public_key.to_string()) }
  15.         }
  16.         fn id(&self) -> i32 { self.id }
  17.         fn public_key(&self) -> String {
  18.             self.public_key.clone().unwrap() // might fail here!
  19.         }
  20.     }
  21.  
  22.     fn connect(port1: WireguardPort, port2: WireguardPort) {
  23.         // do things like we're creating wireguard peer
  24.         let _pubkey1 = port1.public_key();
  25.         let _pubkey2 = port2.public_key();
  26.         println!("Wireguard peer created, {} <-> {}", port1.id(), port2.id());
  27.     }
  28.  
  29.     // the program compiles, but aborts at runtime
  30.     pub fn test() {
  31.         let port1 = WireguardPort::make(1);
  32.         let port2 = WireguardPort::make(2);
  33.         // let port1 = port1.with_public_key("pubkey1");
  34.         // let port2 = port2.with_public_key("pubkey2");
  35.         connect(port1, port2);
  36.     }
  37. }
  38.  
  39. mod with_phantom_types {
  40.     use std::marker::PhantomData;
  41.  
  42.     /// struct with phantom types
  43.     /// public_key may be absent, because the generating of the key pair
  44.     /// may happen later, than the creation of the ports
  45.     struct WireguardPort<T: PubkeyState> {
  46.         id: i32,
  47.         public_key: Option<String>,
  48.         _pd: PhantomData<T>,
  49.     }
  50.  
  51.     /// in Rust this is known as `Typestate pattern`
  52.     trait PubkeyState {}
  53.  
  54.     struct NoPubkey;
  55.  
  56.     struct WithPubkey;
  57.  
  58.     impl PubkeyState for NoPubkey {}
  59.  
  60.     impl PubkeyState for WithPubkey {}
  61.  
  62.     impl<T: PubkeyState> WireguardPort<T> {
  63.         fn id(&self) -> i32 { self.id }
  64.         fn with_public_key(&self, public_key: &str) -> WireguardPort<WithPubkey> {
  65.             WireguardPort { id: self.id, public_key: Some(public_key.to_string()), _pd: PhantomData }
  66.         }
  67.     }
  68.  
  69.     impl WireguardPort<NoPubkey> {
  70.         fn make(id: i32) -> Self { Self { id, public_key: None, _pd: PhantomData } }
  71.     }
  72.  
  73.     impl WireguardPort<WithPubkey> {
  74.         fn public_key(&self) -> String { self.public_key.clone().expect("Impossible") }
  75.     }
  76.  
  77.     fn connect(port1: WireguardPort<WithPubkey>, port2: WireguardPort<WithPubkey>) {
  78.         // do things like we're creating wireguard peer
  79.         let _pubkey1 = port1.public_key();
  80.         let _pubkey2 = port2.public_key();
  81.         println!("Wireguard peer created, {} <-> {}", port1.id(), port2.id());
  82.     }
  83.  
  84.     pub fn test() {
  85.         let port1 = WireguardPort::make(1);
  86.         let port2 = WireguardPort::make(2);
  87.         // Commenting code below does not compile as expected:
  88.         // Type mismatch [E0308]
  89.         // expected `WireguardPortExt<WithPubkey>`, but found `WireguardPortExt<NoPubkey>`
  90.         let port1 = port1.with_public_key("pubkey1");
  91.         let port2 = port2.with_public_key("pubkey2");
  92.         connect(port1, port2);
  93.     }
  94. }
  95.  
  96. pub fn main() {
  97.     without_phantom_types::test();
  98.     with_phantom_types::test();
  99. }
  100.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement