/// 738: PhantomData โ phantom types and type markers
/// PhantomData<T> is zero bytes at runtime but carries T for type checking.
use std::marker::PhantomData;
// โโ Basic phantom type โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// A typed ID: wraps a `u64` but is tagged with a phantom `Entity` type.
/// `UserId` and `OrderId` are different types even though both hold a `u64`.
pub struct Id<Entity> {
value: u64,
_entity: PhantomData<Entity>,
}
impl<Entity> Id<Entity> {
pub fn new(value: u64) -> Self {
Id { value, _entity: PhantomData }
}
pub fn value(&self) -> u64 { self.value }
}
impl<Entity> std::fmt::Debug for Id<Entity> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Id({})", self.value)
}
}
// Entity marker types
pub struct User;
pub struct Order;
pub type UserId = Id<User>;
pub type OrderId = Id<Order>;
// โโ Capability tokens โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pub struct ReadPerm;
pub struct WritePerm;
pub struct Token<Perm> {
id: u32,
_perm: PhantomData<Perm>,
}
impl<Perm> Token<Perm> {
pub fn new(id: u32) -> Self { Token { id, _perm: PhantomData } }
pub fn id(&self) -> u32 { self.id }
}
fn read_resource(_tok: &Token<ReadPerm>, name: &str) -> String {
format!("Reading '{}' with token {}", name, _tok.id())
}
fn write_resource(_tok: &Token<WritePerm>, name: &str, data: &str) -> String {
format!("Writing '{}' = '{}' with token {}", name, data, _tok.id())
}
// โโ Size verification โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
fn main() {
// Typed IDs
let user_id: UserId = Id::new(42);
let order_id: OrderId = Id::new(42);
println!("UserId: {:?}", user_id);
println!("OrderId: {:?}", order_id);
println!("Size of Id<User>: {} bytes", std::mem::size_of::<UserId>());
println!("Size of Id<Order>: {} bytes", std::mem::size_of::<OrderId>());
// The following would NOT compile โ different types despite same inner value:
// let wrong: UserId = order_id; // ERROR: mismatched types
// Capability tokens
let rt: Token<ReadPerm> = Token::new(1);
let wt: Token<WritePerm> = Token::new(2);
println!("{}", read_resource(&rt, "config"));
println!("{}", write_resource(&wt, "config", "debug=true"));
// This would NOT compile:
// read_resource(&wt, "x"); // ERROR: WritePerm โ ReadPerm
// write_resource(&rt, "x", "y"); // ERROR: ReadPerm โ WritePerm
println!("\nPhantomData size: {} bytes", std::mem::size_of::<PhantomData<User>>());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_id_and_order_id_same_value_different_types() {
let uid: UserId = Id::new(1);
let oid: OrderId = Id::new(1);
assert_eq!(uid.value(), oid.value()); // same value, different type
}
#[test]
fn token_read_perm() {
let tok: Token<ReadPerm> = Token::new(42);
let s = read_resource(&tok, "file.txt");
assert!(s.contains("42"));
assert!(s.contains("file.txt"));
}
#[test]
fn token_write_perm() {
let tok: Token<WritePerm> = Token::new(7);
let s = write_resource(&tok, "db", "data");
assert!(s.contains("7"));
assert!(s.contains("data"));
}
#[test]
fn phantom_data_is_zero_sized() {
assert_eq!(std::mem::size_of::<PhantomData<String>>(), 0);
assert_eq!(std::mem::size_of::<PhantomData<Vec<u8>>>(), 0);
}
#[test]
fn id_is_same_size_as_u64() {
assert_eq!(std::mem::size_of::<UserId>(), std::mem::size_of::<u64>());
}
}
(* 738: Phantom Type Basics โ OCaml *)
(* Phantom type to distinguish read vs write tokens *)
type read_perm = Read
type write_perm = Write
(* A capability token โ the 'perm phantom carries the permission *)
type 'perm token = Token of int (* just an id *)
let make_read_token id : read_perm token = Token id
let make_write_token id : write_perm token = Token id
let token_id (Token id) = id
(* Operations gated by permission type *)
let read_data (tok : read_perm token) data =
Printf.printf "Reading with token %d: %s\n" (token_id tok) data
let write_data (tok : write_perm token) data =
Printf.printf "Writing with token %d: %s\n" (token_id tok) data
let () =
let rt = make_read_token 1 in
let wt = make_write_token 2 in
read_data rt "hello";
write_data wt "world";
(* read_data wt "error" โ type error: write_perm token โ read_perm token *)
Printf.printf "Tokens are type-safe!\n"