Rust

Table of Contents

1. Syntax

1.1. Type

There are two ways to construct new types:

  • enum for sum type
  • struct for product type

Types only own the data. The methods are composited by implementing trait.

1.1.1. enum

An enum type takes up the memory space as large as its largest variant.

enum <ident> {
  <ident>(<data types>),
  <ident>,
}

The memory layout of enum contains:

  • tag: small integer for variant identification
  • payload: the actual data.

1.1.2. struct

A struct type holds sequence of data, just like C.

1.1.3. trait

A trait definition declares the method signatures,

trait <ident> {
  fn <ident>(<parameters>) -> <return type>,
}

Actual implementation is needed for each type.

impl <trait ident> for <type ident> {
  fn <ident>(<parameters>) -> <return type> {
    <implementation>
  },
}

A function can be defined on a generic type that implements certain traits

fn <ident>\<T: <traits+>\>(<ident>: T) { ... }

This code does static dispatching, which generates the function for each concrete type on demand, just like C++ template.

Dynamic dispatching is also possible through dyn keyword:

fn <ident>(<ident>: Box\<dyn <trait>\>) { ... }

which calls the function via the function pointer in the vtable generated automatically, similar to C++ virtual method.

Box, & or other indirection is needed in order to fix the size of argument for compilation.

1.1.4. Standard Types

1.1.4.1. Result<T, E>

result can be chained in a monadic way with ? suffix operator. Result provides a collect method that maps Iterator<Result<T, E>> -> Result<Iterator<T>, E>.

E should implement Err trait for fully featured debugging experience.

1.1.4.2. Optional<T>

1.2. Indirection

References (&) necessitate the existence of lifetime for the borrow checker to enforce the memory safety.

1.2.1. Ownership and Borrowing

Only single variable can own a data. Others should use references for the data. Multiple references (&) can exists simultaneously, while there can only be single mutable reference (&mut).

1.2.2. Smart Pointers

Smart pointers are pointer with additional functionality. Some of the smart pointers need extra information stored with the memory address, in which case the pointer is called a fat pointer.

  • Box<T> moves the ownership of the pointer just like C++ std::unique_ptr does.
  • Rc<T> stores reference counts with the pointer.
  • Arc<T> enables sharing pointer across threads. T is required to implement Sync for the ability to be shared across multiple threads.

1.2.3. Lifetime

Lifetime is a range in the source code in which the reference is valid. Lifetime ends when the scope is terminated. Note that empty lines and lines before the assignment does not count.

A generic lifetime for multiple references represents the intersections of all their lifetimes.

fn f<'a>(x: &'a T, y: &'a U) -> V: 'a { ... };

The return value is valid while the all the arguments are valid.

A lifetime constraints can be used for complex case:

fn f<'a, 'b, 'c>(x: &'a T, y: &'b U) -> V: 'c
where
  'a: 'c,
  'b: 'c,
{ ... }    

This means both lifetime 'a and 'b lives longer than 'c.

The default lifetime is indicated by '_, and the lifetime valid throughout the code is indicated by 'static. 'static is used for string literal, and by default Future type.

1.3. Macro

Macro argument delimiter can be any of (), {}, []. The delimiter choice is purely conventional. Best practice is to use:

  • () for function call like macro
  • {} for code block like macro
  • [] for array like macro.

1.3.1. Declarative Macro

Defined with !macro_rules macro with the following syntax:

macro_rules! <macro ident> {
 (<$meta variable>:<fragment specifier>) => {
   <replacement code($meta variable)>
 };
 // more matches...
}

The fragment specifier can be

  • expr
  • ident
  • ty
  • path
  • literal
  • block
  • stmt
  • pat

1.3.2. Procedural Macro

Compile-time function on the token sequence to token sequence. It is usually as parser-retokenization pair with syn and quote crate respectively.

Three form of procedural macro exists:

  • function-like: called inline just like function
  • derive macro: annotated on top of a code block or statement as #[derive(...)]
  • attribute macro: annotated similarly but with more flexibility.

1.4. Async

Rust provides Future type that represents the state machine, but does not runs it. Separate async runtime is required to enable task creation from Future.

A function with async keyword is simply a function that returns Future, with the state machine containing states for each .await statement within the code block.

tokio is the widely used and feature-rich async runtime framework.

2. Toolchain

The rust toolchain can be easily managed by rustup.

2.1. cargo

The RUST crate manager and packager.

2.2. rustc

  • --emit=mir output Mid-level IR
  • --emit=llvm-ir output LLVM intermediate representation
  • --emit=llvm-bc output LLVM Bitcode

3. References

Author: Jeemin Kim

Created: 2026-07-12 Sun 14:27