DerefCallers can use the newtype as if it were the inner type, potentially defeating the purpose Of the wrapper. Only implement Deref when you intentionally want this behavior.
fn to_millimeters ( & self ) -> Millimeters {
Millimeters ( self . 0 * 1000 )
fn to_meters ( self ) -> Meters {
let distance = Meters ( 5 );
let mm = distance . to_millimeters ();
The transparent representation guarantees that the newtype has the same layout as the inner type. This is important for FFI:
fn new (value : u32 ) -> Option < Self > {
The builder pattern constructs complex objects step by step, enforcing required fields at compile Time when using the typestate pattern.
headers : Vec <( String , String )>,
struct HttpRequestBuilder {
headers : Vec <( String , String )>,
impl HttpRequestBuilder {
fn new (method : & str ) -> Self {
method : method . to_string (),
fn url ( mut self , url : & str ) -> Self {
self . url = Some (url . to_string ());
fn header ( mut self , key : & str , value : & str ) -> Self {
self . headers . push ((key . to_string (), value . to_string ()));
fn body ( mut self , body : & str ) -> Self {
self . body = Some (body . to_string ());
fn timeout ( mut self , ms : u64 ) -> Self {
fn build ( self ) -> Result < HttpRequest , String > {
let url = self . url . ok_or ( "url is required" ) ? ;
timeout_ms : self . timeout_ms,
let request = HttpRequestBuilder :: new ( "GET" )
. url ( "https://example.com/api" )
. header ( "Content-Type" , "application/json" )
. header ( "Authorization" , "Bearer token" )
use derive_builder :: Builder ;
#[builder(default = "8080" )]
#[builder(default = r#"String::from("localhost")"# )]
#[builder(setter(into), default = "4" )]
let config = ConfigBuilder :: default ()
. database_url ( "postgres://localhost/mydb" )
The typestate pattern encodes state machines in the type system. Each state is a different type, and State transitions are represented as methods that consume the current state and return the next State. Invalid transitions are compile errors.
struct Configured { host : String , port : u16 }
struct Connected { host : String , port : u16 , stream : std :: net :: TcpStream }
struct Authenticated { host : String , port : u16 , stream : std :: net :: TcpStream , token : String }
impl Client < Unconfigured > {
Client { state : Unconfigured }
fn configure ( self , host : & str , port : u16 ) -> Client < Configured > {
impl Client < Configured > {
fn connect ( self ) -> Result < Client < Connected >, std :: io :: Error > {
let stream = std :: net :: TcpStream :: connect (( self . state . host . as_str (), self . state . port)) ? ;
fn authenticate ( self , username : & str , password : & str ) -> Result < Client < Authenticated >, String > {
let token = format! ( "token_for_{}" , username);
stream : self . state . stream,
impl Client < Authenticated > {
fn send_request ( & self , path : & str ) -> String {
format! ( "GET {} HTTP/1.1 \n Authorization: Bearer {} \n Host: {} \n " ,
path, self . state . token, self . state . host)
let client = Client :: new ()
. configure ( "example.com" , 443 )
. authenticate ( "admin" , "secret" ) ? ;
let request = client . send_request ( "/api/data" );
The type system prevents calling send_request before authenticateOr authenticate before connect. Each method consumes self and returns a new state, making the state transition Irreversible and type-safe.
Attempting an invalid transition is a compile error:
let client = Client :: new ();
// client.send_request("/api"); // ERROR: method not found on Client<Unconfigured>
// client.authenticate("a", "b"); // ERROR: method not found on Client<Unconfigured>
Enum dispatch uses enums to implement polymorphism without trait objects, providing static dispatch And better performance:
Rectangle { width : f64 , height : f64 },
Triangle { base : f64 , height : f64 },
Shape :: Circle { radius } => std :: f64 :: consts :: PI * radius * radius,
Shape :: Rectangle { width, height } => width * height,
Shape :: Triangle { base, height } => 0.5 * base * height,
Property Enum Dispatch Trait Objects (dyn Trait) Dispatch mechanism Static (branch table) Dynamic (vtable indirection) Binary size One copy per variant Shared vtable Extensibility Closed (all variants known) Open (any implementor) Performance Predictable, inlinable Indirect call, not inlinable Type information Full at compile time Erased at runtime
Use enum dispatch when:
The set of variants is known and closed Performance is critical (hot paths, game loops) You need to match on specific variants Use trait objects when:
The set of types is open (plugins, user-defined types) You need heterogeneous collections Binary size is more important than peak performance Zero-sized types occupy no memory at runtime. They are useful as marker types, phantom types, and For compile-time programming.
assert_eq! ( std :: mem :: size_of :: < Marker >(), 0 );
assert_eq! ( std :: mem :: size_of :: < Benchmark >(), 0 );
PhantomData<T> is a zero-sized type that makes the compiler behave as if the struct contains a TEven though it does not. This is useful for variance and drop check annotations:
use std :: marker :: PhantomData ;
let id : Id < String > = Id { value : 42 , _marker : PhantomData };
let id2 : Id < Vec < u8 >> = Id { value : 43 , _marker : PhantomData };
assert_eq! ( std :: mem :: size_of :: < Id < String >>(), 8 );
assert_eq! ( std :: mem :: size_of :: < Id < Vec < u8 >>>(), 8 );
PhantomData<T> affects variance: Id<T> is covariant in T because PhantomData<T> is covariant In T.
ZSTs enable powerful generic programming patterns. A Vec<()> has zero per-element storage cost:
let v : Vec <()> = vec! [(); 1_000_000 ];
assert_eq! (v . len (), 1_000_000 );
// v occupies only the Vec metadata (24 bytes), no element storage
The unit type () is a ZST and is used as a default or placeholder type:
fn process < T >(_ : T ) -> T {
let result = process (()); // T = (), zero overhead
Create a new struct from an existing one, overriding specific fields:
let p1 = Point { x : 1.0 , y : 2.0 , z : 3.0 };
let p2 = Point { y : 5.0 , .. p1 };
// p2.x == 1.0, p2.y == 5.0, p2.z == 3.0
Struct update syntax moves the remaining fields. After ..p1``p1 is partially moved:
let p1 = Point { x : 1.0 , y : 2.0 , z : 3.0 };
let p2 = Point { y : 5.0 , .. p1 };
// println!("{:?}", p1); // ERROR: p1 partially moved
println! ( "{}" , p1 . x); // ERROR: x was moved into p2