DEV Community

Cover image for 30 Days of Rust - Day 25
johnnylarner
johnnylarner

Posted on

30 Days of Rust - Day 25

What is up world, I promised you I'd be back again this week. Today's blog flog will shed some light on Rust's self-proclaimed functional aspects. I'm not really dedicated enough blog time to this topic, but I'm quite time-pressed this evening đź«Ł

Yesterday's questions answered

No questions to answer

Today's open questions

No open questions

5 days left until closure

Anonymous functions in Rust are known as closures. This feature of the language is particularly powerful due to the following fact:

Closures capture variables in the scope at which they are declared

We know that variable scope is fundamental point of thought when designing Rust code. Closures are the mechanism to get around some of the restrictions this poses. One obvious example of where this makes sense is creating a thread. You can make a variable available in that thread's scope.

Fn Traits

Closures (as well as normal functions) can implement so-called Fn Traits:

  • FnOnce -> Called once, moves captured data out when returning
  • FnMut -> Called more than once, may mutate captured values but won't move them
  • Fn -> Called more than once, no mutations, no captured values

These traits allow the Rust compiler to spot issues early. For example, if multiple threads call a closure that mutates a resource that all threads need access too. Similarly, if you move an item in a closure while its original owner is still active in scope, you'd encounter some nasty bugs (if the compiler let you).

Iterators are sexy

Turning collections into iterators opens up a bunch of useful operation to use as a developer. The map, filter and sum methods provide a readable, high-level way of writing code. Consider these two code snippets:

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}


pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.contains(query) {
            results.push(line)
        }
    } 

    results
}
Enter fullscreen mode Exit fullscreen mode

Not only is the first example shorter, the code is more expressive of the procedural nature of the task. What's more, we don't have to worry about state management (of the vector) which could become challenging in a multi-threaded system.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Eliminate Context Switching and Maximize Productivity

Pieces.app

Pieces Copilot is your personalized workflow assistant, working alongside your favorite apps. Ask questions about entire repositories, generate contextualized code, save and reuse useful snippets, and streamline your development process.

Learn more

đź‘‹ Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay