Skip to content

Essential Rust Syntax Explained

Attention

For an amazing article on collections in Rust, their complexities, and when/why to use them read the docs for std::collections here.

if let

When extracting inner content from an enum, such as a Result or Option, if you're only interested in one of the variants then it's cleaner to use an if let statement. For example,

rust
// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
    process::exit(1);
}
// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
    process::exit(1);
}

Read the statement if let Err(e) = result as: If result is an Err variant, pass the error e to the block of code, otherwise skip it.

while let

This syntax is the looping equivalent of if let. It will continue to loop until the while condition fails. For example,

rust
// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
	// do some processing
}
// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
	// do some processing
}
Attention

You cannot write to a variable in a loop that you don't own. Make sure to take ownership of a variable before you try to write to it in-place in a loop.

loop

Use this for an infinite loop. It is particularly useful from daemon processes.

dyn

This is used when declaring a trait as a parameter. Since the size of the object implementing the trait can't be known at compile time, it has to be cast onto the heap using Box<dyn Trait> syntax.

.into_*

When you call a method that is prefixed with into, such as .into_iter(), it consumes the original object, returning the requested type, in this case, an iterator. This is distinct from the raw .iter() call, which instead iterates over a reference, providing a view of the data without consuming it.

Rc and Arc

These are used to make shared pointers to data (reference counted).

Cloning
  • Cloning shared pointers is cheap, so don't be afraid
  • Always pass a reference when cloning an Rc or Arc, e.g., Rc::clone(&value)

match

You can match against two variables....

rust
fn main() {
    let pair = (5, 10);

    match pair {
        (0, 0) => println!("Both elements are zero"),
        (_, 0) => println!("The second element is zero"),
        (0, _) => println!("The first element is zero"),
        (x, y) => println!("Both elements are non-zero: {}, {}", x, y),
    }
}
fn main() {
    let pair = (5, 10);

    match pair {
        (0, 0) => println!("Both elements are zero"),
        (_, 0) => println!("The second element is zero"),
        (0, _) => println!("The first element is zero"),
        (x, y) => println!("Both elements are non-zero: {}, {}", x, y),
    }
}