ExamplesBy LevelBy TopicLearning Paths
740 Advanced

740-phantom-variance-control — Phantom Variance Control

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "740-phantom-variance-control — Phantom Variance Control" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. Variance describes how subtyping relationships propagate through generic type constructors. Key difference from OCaml: 1. **Syntax**: OCaml uses `+'a` / `

Tutorial

The Problem

Variance describes how subtyping relationships propagate through generic type constructors. In Rust, lifetimes and types can be covariant, contravariant, or invariant in a type parameter. Getting variance wrong leads to unsound code: a Cell<&'static str> being treated as Cell<&'short str> would allow writing a short-lived reference into a long-lived cell. PhantomData is the tool for explicitly setting variance when the compiler cannot infer it correctly from the struct fields, especially when raw pointers are involved.

🎯 Learning Outcomes

  • • Understand covariance, contravariance, and invariance in Rust's type system
  • • Use PhantomData<T> for covariance (acts like owning a T)
  • • Use PhantomData<fn(T)> for contravariance (acts like a function consuming T)
  • • Use PhantomData<Cell<T>> or PhantomData<*mut T> for invariance
  • • Know when raw pointer fields make variance inference go wrong
  • Code Example

    #![allow(clippy::all)]
    //! # Phantom Variance Control
    
    pub fn placeholder() -> &'static str {
        "phantom-variance-control implementation"
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }

    Key Differences

  • Syntax: OCaml uses +'a / -'a variance annotations directly; Rust encodes variance through the type of the PhantomData field.
  • Clarity: OCaml's annotations are self-documenting; Rust's PhantomData<fn(T)> for contravariance is a non-obvious idiom requiring documentation.
  • Inference: Rust infers variance automatically for structs without raw pointers; OCaml requires explicit annotations on abstract types.
  • Soundness: Both enforce variance at the type-checker level with no runtime cost.
  • OCaml Approach

    OCaml uses variance annotations directly on type parameters: type +'a t (covariant), type -'a t (contravariant), and type 'a t (invariant by default). The compiler verifies that the annotation matches the actual usage. OCaml's explicit variance annotations are more readable than Rust's PhantomData trick. The Base library's Container types use precise variance annotations to enable safe covariant use.

    Full Source

    #![allow(clippy::all)]
    //! # Phantom Variance Control
    
    pub fn placeholder() -> &'static str {
        "phantom-variance-control implementation"
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }

    Deep Comparison

    Variance Control

    See example files for comparison.

    Exercises

  • Write a Writer<T> type that is contravariant in T — it accepts a T but produces nothing — and verify that Writer<&'short str> can be used as Writer<&'static str>.
  • Implement a Buffer<T> type that must be invariant in T because it both reads and writes T. Use PhantomData<Cell<T>> and explain why invariance is required.
  • Create a Producer<T> (covariant) and Consumer<T> (contravariant) and combine them into a Channel<T> that is invariant, demonstrating variance composition.
  • Open Source Repos