🖥️...⌨️

Comprehensive Rust Guide and Common Pitfalls


1. Ownership, Borrowing, and Lifetimes

The core of Rust’s safety guarantees is its ownership model. Every value has a single owner, and when that owner goes out of scope, the value is dropped. You can transfer ownership (“move”) or create borrows—immutable (&T) or mutable (&mut T).

Misusing borrows leads to common pitfalls:

Rust’s lifetime elision rules simplify function signatures but hide implicit lifetime bounds. When in doubt, annotate lifetimes explicitly, e.g.:

fn join_str<'a>(a: &'a str, b: &'a str) -> String { … }


2. Data Types, Collections, and Iterators

Rust’s primitive types (i32, bool, char) are complemented by powerful built-ins: Option<T>, Result<T, E>, and collections like Vec<T>, HashMap<K, V>.

Iterators unify traversal and transformation. The Iterator trait provides methods like map, filter, and collect. Beware:

Example:

let nums = vec![1,2,3];
let doubled: Vec<_> = nums.iter().map(|n| n * 2).collect();


3. Error Handling Patterns

Rust eschews exceptions in favor of Result<T, E> and the ? operator. Functions that may fail typically return Result.

Pitfalls and best practices:

Example with ?:

fn read_number(path: &str) -> Result<i32, std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    let num = content.trim().parse::<i32>().map_err(|e| std::io::Error::new(...))?;
    Ok(num)
}


4. Modules, Crates, and Cargo

Rust projects are organized into crates (packages) and modules. The src/lib.rs or src/main.rs is the crate root. Use mod to define a module, pub to export items, and use to import.

Cargo features:

Common pitfalls include circular module imports and forgetting to declare items pub, leading to private-module errors.


5. Traits, Generics, and Abstractions

Generics and traits power polymorphism. Define trait bounds to ensure type capabilities:

fn print_all<T: std::fmt::Display>(items: &[T]) {
    for item in items { println!("{}", item); }
}

Watch out for:


6. Macros and Code Generation

Rust offers declarative macros (macro_rules!) and procedural macros (custom derive, function-like, attribute). Macros reduce boilerplate but complicate debugging.

Best practices and pitfalls:

Example macro_rules:

macro_rules! try_log {
    ($expr:expr) => {
        match $expr {
            Ok(v) => v,
            Err(e) => { log::error!("{}", e); return Err(e.into()); }
        }
    }
}


7. Async Programming with Tokio

Rust’s async model uses async/await and futures. Tokio is the de facto async runtime. Annotate your main with #[tokio::main] and spawn tasks via tokio::spawn.

Key pitfalls:

Example:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let handle = tokio::spawn(async { heavy_compute().await });
    let result = handle.await?;
    Ok(())
}


8. Working with serde_json

serde_json provides flexible JSON parsing and serialization built on serde. Core types: serde_json::Value, Map<String, Value>.

Convenience functions and abstraction patterns:

Common pitfalls:


9. Testing, Benchmarking, and Documentation

Rust integrates testing and documentation:

Benchmarking uses crates like criterion. Document public APIs with /// comments and examples; examples run on cargo test.

Pitfalls:


10. Performance and Common “Gotchas”

Rust’s zero-cost abstractions mostly pay for themselves, but watch for:

Use cargo flamegraph, tokio-console, or tracing + perf to profile.


11. Common Utility Crates


Whether you’re diving into async servers with Tokio, sculpting data shapes via serde_json, or mastering lifetimes, Rust rewards precision and foresight. Its compiler is your guide—read and heed its errors. Embrace small iterative refactors, write idiomatic patterns, and lean on the community’s rich crate ecosystem. Your Rust code will become safer, faster, and increasingly elegant.

Beyond this, you may explore advanced topics such as unsafe code patterns, FFI boundaries, embedded targets, and Rust’s macro 2.0. Each area deepens both safety and power.

Happy coding! For further reading, see “The Rust Programming Language” (a.k.a. The Book) and the official Tokio and Serde JSON guides.

12. Unsafe Rust and FFI


Rust’s safety guarantees can be relaxed with the unsafe keyword. This unlocks:

When crossing language boundaries (FFI), unsafe is inevitable. Common patterns:

extern "C" {
    fn strlen(s: *const libc::c_char) -> libc::size_t;
}

unsafe {
    let len = strlen(c_string.as_ptr());
}

Pitfalls:

Best practices:

13. Build Scripts (build.rs) and Code Generation


Cargo’s build scripts let you generate code or link external libraries at compile time. Typical uses:

Example build.rs:

fn main() {
    println!("cargo:rerun-if-changed=wrapper.h");
    bindgen::builder()
        .header("wrapper.h")
        .generate()
        .expect("bindgen failed")
        .write_to_file("src/bindings.rs")
        .expect("failed to write bindings");
}

Pitfalls:

14. Procedural Macros Deep Dive


Procedural macros extend syntax with custom derive, attribute-like, and function-like macros. They run at compile time in a separate crate annotated with proc-macro = true.

Structure:

Example derive skeleton:

#[proc_macro_derive(Builder)]
pub fn derive_builder(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as DeriveInput);
    // transform AST, build TokenStream
    quote!( /* generated code */ ).into()
}

Pitfalls:

15. Embedded Rust and no_std Environments


In constrained environments (microcontrollers, kernels), standard library is unavailable. Use #![no_std] and crates like cortex-m-rt, embedded-hal.

Key points:

Pitfalls:

16. Concurrency Patterns Beyond Tokio


While Tokio dominates async, CPU-bound parallelism shines with Rayon:

use rayon::prelude::*;

let sum: i32 = (0..1_000_000).into_par_iter().sum();

Other patterns:

Pitfalls:

17. Profiling, Optimization, and Release Builds


Fine-tune performance with Cargo profiles:

Profile Opt Level Debug Info LTO Codegen Units
dev 0 true off 256
release 3 false off 16
bench 3 true off 16
custom variable variable on 1

Tools:

Pitfalls:

18. Continuous Integration and Deployment


Automate quality with CI/CD:

Pitfalls:

19. Design Patterns and Idioms


Rust has its own take on classic patterns:

Pitfalls:


Beyond these topics, consider diving into:

The Rust ecosystem is vast—keep exploring, profiling, and refactoring.

20. Deep Dive into Borrowing, References, and Mutability


20.1 Immutable References (&T)

Every shared read-only view into a value uses &T. You can have any number of simultaneous &T borrows, as long as no &mut T exists.

Example:

fn sum(slice: &[i32]) -> i32 {
    slice.iter().sum()
}

let data = vec![1, 2, 3];
let total = sum(&data); // data is immutably borrowed
println!("{}", total);
println!("{:?}", data); // data is still usable afterward

Common pitfalls:


20.2 Mutable References (&mut T)

A mutable reference grants exclusive, writeable access to a value. The borrow checker enforces that at most one &mut T exists at a time, and no &T co-exists concurrently.

Example:

fn increment(x: &mut i32) {
    *x += 1;
}

let mut val = 10;
increment(&mut val);
println!("{}", val); // prints 11

Key rules:


20.3 Reborrowing and Scoped Borrows

Reborrowing lets you pass a shorter borrow to a sub-function without relinquishing the original borrow entirely:

fn foo(x: &mut String) {
    bar(&mut *x);      // reborrow as &mut str
    println!("{}", x); // original borrow resumes afterward
}

fn bar(s: &mut str) { s.make_ascii_uppercase(); }

Pitfalls:


20.4 Non-Lexical Lifetimes (NLL)

Rust’s NLL relaxes borrowing scopes: borrows end where they’re last used, not at end of scope. This lets your code compile in more cases:

let mut v = vec![1,2,3];
let x = &v[0];
println!("{}", x);       // borrow of `v` ends here
v.push(4);               // now allowed

Without NLL, v.push(4) would conflict with x’s borrow.


20.5 Common Pitfalls with &mut


21. Interior Mutability: Cell, RefCell, Mutex, RwLock

When you need to mutate data behind an immutable reference (e.g., shared caches, lazily-computed fields), Rust offers interior-mutability types. They defer borrow checks to runtime or use locking.

Type Borrow Check Thread Safety Use Case
Cell<T> No borrows, copy Single-thread Copy-able values, fine-grained updates
RefCell<T> Runtime borrow tracking Single-thread Complex data with occasional mutability
Mutex<T> OS-level lock Multi-thread Shared mutable state across threads
RwLock<T> Read/write lock Multi-thread Many readers, few writers

Example with RefCell:

use std::cell::RefCell;

struct Cache {
    map: RefCell<HashMap<String, String>>,
}

impl Cache {
    fn get(&self, key: &str) -> Option<String> {
        if let Some(v) = self.map.borrow().get(key) {
            return Some(v.clone());
        }
        let new = expensive_compute(key);
        self.map.borrow_mut().insert(key.to_string(), new.clone());
        Some(new)
    }
}

Pitfalls:


22. Mutable Aliasing and the “You Cannot”

Rust forbids mutable aliasing—two pointers that can modify the same data simultaneously—because it leads to data races or unexpected behavior. You’ll see errors like:

cannot borrow `x` as mutable more than once at a time

Workarounds:


23. Borrow Checker in Generic Code

When writing generic functions, be explicit with lifetimes to avoid “missing lifetime specifier” errors:

fn tie<'a, T>(x: &'a mut T, y: &'a mut T) {
    // ERROR: you cannot have two &mut T with the same 'a!
}

Solution: give distinct lifetimes or restrict usage:

fn tie<'x, 'y, T>(x: &'x mut T, y: &'y mut T) { /* … */ }


24. Best Practices and Tips


25. Further Exploration

Borrowing is the heart of Rust’s safety. Embrace the compiler’s rules, sculpt your data structures to express clear ownership, and let the borrow checker guide you toward bug-free, concurrent systems.

26. PhantomData, Variance, and Zero-Sized Types

PhantomData lets you declare “ghost” ownership or borrowing without storing data. It’s critical for encoding lifetimes or variance in generic types.

use std::marker::PhantomData;

struct MySlice<'a, T: 'a> {
  ptr: *const T,
  len: usize,
  _marker: PhantomData<&'a T>,
}

Pitfall: forgetting PhantomData leads to soundness holes or unexpected variance.


27. Pin, Unpin, and Self-Referential Structs

Pin prevents data from moving in memory, enabling safe self-referential types (e.g., futures that point to fields within themselves).

use std::pin::Pin;
use std::future::Future;

struct MyFuture {
  // this future holds a string and a pointer into it
  data: String,
  pos: *const u8,
}

// Safely project MyFuture fields under Pin

Pitfalls: misuse of Pin::into_inner_unchecked can break safety. Always wrap unsafe projections in a stable, audited API.


28. Generic Associated Types (GATs) and Advanced Lifetimes

GATs let you tie an associated type to a lifetime parameter:

trait StreamingIterator {
  type Item<'a> where Self: 'a;
  fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

Use cases: streaming parsers or iterators that return references to internal buffers.

Pitfalls: compiler errors on missing where clauses or forgetting #![feature(generic_associated_types)] on nightly.


29. Capturing Borrows in Closures (Fn, FnMut, FnOnce)

Closures choose their Fn traits by how they capture variables:

let mut x = 1;
let mut inc = || { x += 1; }; // captures x by &mut
inc();

Pitfalls: passing an FnMut closure to an API expecting Fn leads to a trait‐bound error. Use .by_ref() or change the signature to impl FnMut(_).


30. Smart Pointers and DerefMut

Rust offers Box, Rc, Arc with Deref and DerefMut impls:

let mut boxed: Box<Vec<i32>> = Box::new(vec![1,2,3]);
boxed.push(4); // DerefMut to Vec<i32>

Pitfalls: unexpected clone of Arc then forgetting to lock the inner Mutex.


31. &mut Across Threads: Send + Sync Bounds

A &mut T is always !Sync—you cannot share it across threads. If you need mutation across threads:

Pitfalls: using raw &mut in a thread spawn will not compile, but replacing it with Arc without locking leads to data races.


32. Atomic Types and Memory Ordering

For lock-free mutation, Rust has atomic primitives:

use std::sync::atomic::{AtomicUsize, Ordering};

static COUNTER: AtomicUsize = AtomicUsize::new(0);
COUNTER.fetch_add(1, Ordering::SeqCst);

Pitfalls: misuse of Relaxed can silently reorder operations across threads—always document the reasoning.


33. Procedural Macros for Borrow Check Boilerplate

When exposing an API that takes multiple &mut arguments, you can auto-generate safe wrappers:

#[derive(MutBorrow)] // custom derive you write
struct Gui {
  button: Button,
  label: Label,
}
// expands to Fn(&mut Gui) -> (&mut Button, &mut Label)

Pitfalls: debugging generated code demands reading the expanded output (cargo expand).


34. Macro_rules! Patterns for &mut Matching

Declarative macros can match on mutability:

macro_rules! with_mut {
  ($mutability:ident $var:ident, $body:block) => {
    $mutability $var;
    $body
  };
}
with_mut!(mut x, { x += 1; });

Pitfalls: hygiene issues—unexpected shadowing if you don’t use local macro-specific names.


35. Clippy Lints to Catch Borrowing Smells

Enable or audit these lints:

Regularly run cargo clippy --all-targets -- -D warnings to enforce correct borrow usage.


Beyond these, explore Polonius (the future of borrow checking), Miri for detecting undefined behavior, and the Rust compiler’s borrow-checker internals to master every nuance.

36. WebAssembly Targets with wasm-bindgen

Rust compiles to WebAssembly (WASM) for web and edge applications.

Example:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

Pitfalls:


37. Building GraphQL Servers with async-graphql

async-graphql harnesses Rust’s type system to define schemas:

Example:

#[derive(SimpleObject)]
struct User { id: ID, name: String }

struct QueryRoot;

#[Object]
impl QueryRoot {
  async fn user(&self, ctx: &Context<'_>, id: ID) -> Option<User> { … }
}

Pitfalls:


38. Domain-Driven Design (DDD) in Rust

DDD patterns map naturally onto Rust’s ownership:

Pitfalls:


39. Serialization Performance Tuning

High-throughput systems need lean serializers:

Pitfalls:


40. Working with YAML/TOML via Serde

Beyond JSON, serde supports YAML (serde_yaml) and TOML (toml crate):

Pitfalls:


41. Advanced Testing Patterns

Scale your tests using:

Pitfalls:


42. Mocking and Dependency Injection

Rust lacks built-in mocks but offers crates:

Pitfalls:


43. Crate Features and Conditional Compilation

Leverage Cargo’s features to toggle functionality:

Pitfalls:


44. Workspace Design and Release Strategies

Group related crates in a workspace for shared dependencies:

Pitfalls:


45. Plugin and Extension Architectures

Create dynamic plugin systems with:

Pitfalls:


46. Distributed Systems Patterns

Rust’s safety complements distributed design:

Pitfalls:


47. Microservices and Service Mesh with tower

The tower ecosystem provides modular middleware:

Pitfalls:


48. Actor Frameworks (actix, riker)

Actor models map nicely to async Rust:

Pitfalls:


49. Dependency Injection Frameworks (shaku, inversion)

Rust’s DI crates allow runtime wiring:

Pitfalls:


50. Monitoring, Tracing, and Telemetry

Rust’s tracing crate provides structured telemetry:

Pitfalls:

61. Custom Global Allocators

Rust lets you override the default memory allocator to tune performance or integrate specialized allocators.

use jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;

Pitfalls:


62. Memory Profiling and Leak Detection

Track heap usage and leaks in Rust programs:

Pitfalls:


63. Designing Custom Thread Pools

While Tokio and Rayon cover most use cases, you can build bespoke pools:

use crossbeam::queue::SegQueue;
use std::thread;

struct ThreadPool { /* worker threads, task queue */ }

Pitfalls:


64. Concurrency Testing with Loom

Loom exhaustively explores thread interleavings on your concurrent code to catch data races and deadlocks.

loom::model(|| {
    let lock = loom::sync::Mutex::new(0);
    let guard = lock.lock().unwrap();
    // test your critical-section logic here
});

Pitfalls:


65. Fuzz Testing with cargo-fuzz and AFL

Automate input‐driven testing to discover edge‐case bugs:

Pitfalls:


66. Panic Strategies and No‐Unwind Environments

Control panic behavior in binaries and libraries:

  #[panic_handler]
  fn panic(info: &PanicInfo) -> ! { loop {} }

Pitfalls:


67. Embedding Scripting Languages

Add runtime extensibility by embedding interpreters:

Pattern:

let engine = rhai::Engine::new();
engine.eval::<i64>("40 + 2")?;

Pitfalls:


68. Transactional and Persistent Data Structures

Explore lock‐free and crash‐safe structures:

Pitfalls:


69. Typestate and Zero‐Cost State Machines

Leverage the type system to enforce protocol state at compile time:

struct Connection<St> { /* ... */ }
struct Disconnected;
struct Connected;

impl Connection<Disconnected> {
  fn connect(self) -> Connection<Connected> { … }
}

impl Connection<Connected> {
  fn send(&self, data: &[u8]) { … }
  fn disconnect(self) -> Connection<Disconnected> { … }
}

Pitfalls:


70. Multi‐Language Interop with CXX and Uniffi

For safe, ergonomic bridges to C++, Swift, Kotlin:

  #[cxx::bridge]
  mod ffi {
      extern "Rust" { fn rust_fn(x: i32) -> i32; }
      extern "C++" { fn cpp_fn(x: i32) -> i32; }
  }

Pitfalls:


Say “continue” to unlock items 71–80 and dive even deeper into Rust’s frontier.

71. Polonius Borrow Checker Experiments

Rust’s next‐generation borrow checker, Polonius, refines non‐lexical lifetimes and region inference at the MIR level. It exposes more flexible borrow scopes and better diagnostics.

Pitfalls:


72. Miri for Unsafe-Code Verification

Miri is an interpreter that checks your code for undefined behavior at the MIR level, including strict pointer provenance and UB in unsafe blocks.

Pitfalls:


73. Dynamic Code Inclusion with include! and include_str!

Rust macros let you embed external code or assets at compile time:

include!("generated/config.rs");
static SCHEMA: &str = include_str!("schema.graphql");

Pitfalls:


74. Fine-Grained Editor Integration and LSP Tips

To maximize productivity, configure your editor’s Rust plugin:

Pitfalls:


75. Security Auditing and Fuzz-AFL Integration

Beyond functional correctness, audit your crate’s dependencies and surface code:

Pitfalls:


76. Crate Governance, Ownership, and Contribution Workflow

Maintain a healthy open-source project by defining clear policies:

Pitfalls:


77. Versioning, Release Channels, and SemVer Discipline

Rust crates follow semantic versioning—major.minor.patch—to signal compatibility:

Pitfalls:


78. API Design Guidelines and Rustdoc Style

Craft ergonomic public interfaces and documentation:

Pitfalls:


79. Internationalization and Localization

Rust’s ecosystem offers crates for i18n:

Pitfalls:


80. Continuous Exploration: Rust RFCs and Nightly Channels

Stay at the cutting edge by tracking:

Pitfalls:


Say “continue” to reveal items 81–90 and keep deepening your mastery of Rust’s vast ecosystem.

81. Diverging Functions and the never Type (!)

Rust’s diverging functions—those that never return—use the “never” type !. They serve two roles: signaling an endpoint in control flow and enabling exhaustive matching.

Functions that always panic or loop indefinitely are natural !:

fn infinite_loop() -> ! {
    loop {
        // do work forever
    }
}

fn fail(msg: &str) -> ! {
    panic!("Fatal error: {}", msg);
}

At call sites, ! coerces into any other return type, letting you write concise error handlers:

fn parse_or_panic(s: &str) -> i32 {
    s.parse().unwrap_or_else(|_| panic!("Invalid number"))
}

Pitfalls:


82. Async Traits with the async_trait Crate

Rust doesn’t yet support async functions directly in traits, but the async_trait macro makes it ergonomic:

#[async_trait::async_trait]
pub trait Store {
    async fn insert(&self, key: String, value: String) -> Result<()>;
}

struct MyStore;
#[async_trait::async_trait]
impl Store for MyStore {
    async fn insert(&self, key: String, value: String) -> Result<()> {
        // perform async I/O here
        Ok(())
    }
}

Under the hood, async_trait boxes the returned future and hides lifetime gymnastics.

Pitfalls:


83. Safe Global State with OnceCell and Lazy

Global mutable state is tricky in Rust, but crates like once_cell and the standard Lazy wrapper provide thread-safe one-time initialization:

use once_cell::sync::Lazy;
static CONFIG: Lazy<Config> = Lazy::new(|| {
    // expensive parse at first access
    Config::from_file("config.toml").unwrap()
});

After that, *CONFIG is immutable and safe across threads.

Pitfalls:


84. Zero-Copy Deserialization with Borrowed Data

When parsing JSON or YAML for performance, you can borrow directly from the input buffer:

#[derive(Deserialize)]
struct Message<'a> {
    id: &'a str,
    #[serde(borrow)]
    tags: Vec<&'a str>,
}

let data = r#"{"id":"abc","tags":["x","y"]}"#.to_string();
let msg: Message = serde_json::from_str(&data)?;

The deserializer reuses the original data buffer without allocating new strings for every field.

Pitfalls:


85. Bincode and Binary Serialization Pitfalls

Binary formats like bincode excel at compactness and speed, but expose low-level concerns:

let encoded: Vec<u8> = bincode::serialize(&my_struct)?;
let decoded: MyStruct = bincode::deserialize(&encoded)?;

Pitfalls:


86. Designing Mini-DSLs with Macros

Macros can define small domain-specific languages (DSLs) that expand into Rust code:

macro_rules! sql {
    ($table:ident . $col:ident == $val:expr) => {
        format!("SELECT * FROM {} WHERE {} = {}", stringify!($table), stringify!($col), $val)
    };
}

let q = sql!(users.id == 42);
// expands to "SELECT * FROM users WHERE id = 42"

Pitfalls:


87. Embedding SQL with sqlx::query!

The sqlx crate provides compile-time checked queries:

let row = sqlx::query!("SELECT name, age FROM users WHERE id = $1", user_id)
    .fetch_one(&pool)
    .await?;
let name: String = row.name;

Pitfalls:


88. Database Transactions and Connection Pools

Maintain data integrity and performance:

let mut tx = pool.begin().await?;
sqlx::query!("UPDATE accounts SET balance = balance - $1 WHERE id = $2", amt, id)
    .execute(&mut tx)
    .await?;
tx.commit().await?;

Pitfalls:


89. Scheduled Tasks with tokio::time

Perform periodic work with Tokio’s timers:

use tokio::time::{self, Duration};

let mut interval = time::interval(Duration::from_secs(60));
loop {
    interval.tick().await;
    check_system_metrics().await;
}

Pitfalls:


90. HTTP Clients with Reqwest

Build HTTP requests with connection reuse and timeout control:

let client = reqwest::Client::builder()
    .timeout(Duration::from_secs(10))
    .build()?;
let resp = client.get(url).send().await?;

Pitfalls:


91. Rate Limiting with tower Middleware

Protect your services with leaky‐bucket throttling:

use tower::ServiceBuilder;
use tower::limit::RateLimitLayer;

let svc = ServiceBuilder::new()
    .layer(RateLimitLayer::new(5, Duration::from_secs(1)))
    .service(my_service);

Pitfalls:


92. Fallback and Retry Patterns with tower

Compose robust services that retry or fallback on errors:

use tower::retry::{Retry, Policy};
let retry_policy = MyPolicy::default();
let svc = Retry::new(retry_policy, base_service);

Pitfalls:


93. Context Propagation with tracing Spans

Carry telemetry context across async boundaries:

#[tracing::instrument]
async fn handle_request(req: Request) -> Response {
    // all logs inside carry this span’s fields
}

Pitfalls:


94. In-Process Plugins via Dynamic Loading

Load shared-object plugins at runtime:

let lib = libloading::Library::new("plugin.so")?;
let func: libloading::Symbol<unsafe extern "C" fn()> = lib.get(b"run")?;
unsafe { func(); }

Pitfalls:


95. Runtime Reflection with TypeId and Any

Although limited, Rust allows some type introspection:

use std::any::{Any, TypeId};

fn is_string(val: &dyn Any) -> bool {
    val.type_id() == TypeId::of::<String>()
}

Pitfalls:


96. Phantom Types for Compile-Time Invariants

Beyond PhantomData, phantom types enforce compile-time rules without runtime cost:

struct Length<Unit> { value: f64, _marker: PhantomData<Unit> }
struct Meters;
struct Seconds;

type Speed = Length<Meters>;

// You can’t add Length<Seconds> to Length<Meters>—the types differ.

Pitfalls:


97. FFI Symbol Visibility and Name Mangling

When exposing Rust functions to C or other languages, control symbol exports:

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

Pitfalls:


98. Panic-Unwind ABI and Cross-Crate Boundaries

Rust’s default panic strategy is “unwind,” but C++ or other languages may misinterpret it:

Pitfalls:


99. Slimming Binaries and Linker Optimizations

Reduce your compiled size for embedded or WASM targets:

Pitfalls:


100. Crate Metadata, Licensing, and Publication Best Practices

A well-crafted Cargo.toml signals professionalism:

[package]
name = "my_crate"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/you/my_crate"

[badges]
travis-ci = { repository = "you/my_crate" }

Pitfalls:


Thank you for journeying through one hundred facets of Rust programming, from core borrowing rules to FFI intricacies, async patterns to crate governance. Armed with these templates, caveats, and advanced techniques, you’ll write Rust code that’s safe, efficient, and future-proof. Happy coding, and may the borrow checker always be in your favor!