• Discover
  • Partner with us
  • Chapters
  • Blog

Learn everything about Rust

Why Rust?
✨Connect Your Profile to Open Campus
Cargo
Introduction to Rust syntax
Basic data types and variables
Functions and control flow

Ownership concept
Borrowing and References
Clone Function
Memory safety in Rust
Task: Implement a basic program that uses ownership concepts

Structs
Enums
Simple introduction to Option and Result
Common collections in Rust
Task: Create a simple calculator using enums and pattern matching

Traits
(Advanced) Trait Objects and Box Smart Pointer
Introduction to Generics and its usage in functions
Implementation of Generics using structs and enums in Rust
(Advanced) Lifetimes
Task: Creating a basic banking system using Traits

Introduction to Iterator and its types in Rust
Understanding the usage of Iterators with loops
(Advanced) Closures
Modules and visibility
Task: Building a Custom Filtering Function in Rust

Panic! macro
Error handling
Creating and using custom error types
Task: Adding error handling to the basic banking system

Final project

Welcome to the closures for Rust tutorial! In this tutorial, you will learn what closures are, how to create and use them, and why they are awesome. By the end of this tutorial, you will be able to use closures under any circumstances and impress your friends with your Rust skills.

What are closures?

Closures are functions without names that can capture the enclosing environment. They are also known as anonymous functions or lambdas. You might be wondering why you would need a function without a name. Well, there are many situations where you want to create a custom function on the fly, without having to define it separately and give it a name. For example, you might want to pass a function as an argument to another function, or return a function from a function, or store a function in a variable. Closures allow you to do all these things and more.

Unlike functions, closures can capture values from the scope in which they are defined. This means that you can access and manipulate variables that are outside the closure's body. This is very useful for creating dynamic and flexible functions that can adapt to different contexts.

How to create and use closures?

To create a closure in Rust, you use the `||` syntax around the input variables, followed by the body of the closure. For example:

// define a closure to print a text
let print_text = || println!("Defining Closure");

In this example, `print_text` is a variable that stores the closure, and `println!("Defining Closure")` is the body of the closure that prints the text "Defining Closure" when called.

To call a closure, you use the same syntax as calling a function: just add parentheses after the closure's name and pass any arguments inside them. For example:

// call the closure
print_text();

This will print "Defining Closure" to the console.

You can also pass parameters to a closure, just like a function. For example:

// define a closure that takes an integer and returns a boolean
let is_even = |x: i32| -> bool { x % 2 == 0 };

In this example, `is_even` is a closure that takes an integer parameter `x` and returns a boolean value indicating whether `x` is even or not.

You can call this closure with any integer value and get the corresponding result. For example:

// call the closure with different values
println!("Is 2 even? {}", is_even(2)); // true
println!("Is 3 even? {}", is_even(3)); // false

A quick note: In the older versions of the Rust, we did not have to specify the type of the closure and Rust could have infer it. With the newer versions this is not possible.

The Magical Trio: Fn, FnMut, and FnOnce

Now let's talk about Fn, FnMut, and FnOnce, the magical trio of traits that let us use closures in Rust. What are they, exactly?

  • FnOnce: This trait represents closures that can be called exactly once. They may move (consume) values from their environment. It's like a one-time party invitation – you use it, and it's gone.
  • FnMut: This trait is for closures that can be called multiple times and can mutate values from their environment. It's like having a key to the house – you can enter as many times as you want, and you're allowed to move the furniture.
  • Fn: This trait is for closures that can be called multiple times without mutating their environment. It's like being a ghost – you can pass through the house as much as you want, but you can't change anything.

So, when we use these traits as bounds for our generic F in the apply function, we're telling Rust what kind of closure we're expecting. And as always, Rust is pretty understanding about it!

Why are closures awesome?

Closures are awesome because they allow you to create custom functions that suit your needs and adapt to different contexts. You can use them for tasks such as filtering, sorting, mapping, or reducing data collections. You can also use them for implementing callbacks, iterators, or generators.

For example, let's say you have a vector of numbers, and you want to filter out only the even ones. You can use the `filter` method on the vector and pass a closure as an argument that checks if a number is even. For example:

// define a vector of numbers
let numbers = vec![1, 2, 3, 4, 5];

// filter out only the even numbers using a closure
let evens: Vec<i32> = numbers.into_iter().filter(|x| x % 2 == 0).collect();

// print the filtered vector
println!("The even numbers are: {:?}", evens); // [2, 4]

In this example, we use the `into_iter` method to create an iterator over the vector elements, then we use the `filter` method to apply a closure that returns true if `x` is even and false otherwise. Finally, we use the `collect` method to convert the iterator into a vector.

As you can see, closures make it easy and concise to write custom logic for filtering data.

Another example of using closures is for implementing callbacks. A callback is a function that is passed as an argument to another function and is executed at some point during the execution of that function. For example, let's say you have a function that downloads some data from a website and you want to execute some code after the download is complete. You can use a closure as a callback argument and pass it to the download function. For example:

    // define a function that downloads some data from a website
    fn download_data(url: &str, callback: impl FnOnce(&str)) {
        // simulate downloading data by printing some text
        println!("Downloading data from {}...", url);

        // simulate some delay by sleeping for one second
        std::thread::sleep(std::time::Duration::from_secs(1));

        // simulate some data by creating a string
        let data = format!("Some data from {}", url);

        // execute the callback with the data as an argument
        callback(&data);
    }

    // define a closure that prints some text after receiving data
    let print_data = |data: &str| {
        println!("Received data: {}", data);
    };

    // call the download function with a url and a closure as arguments
    download_data("https://www.rust-lang.org", print_data);

In this example, we define a function `download_data` that takes two arguments: a url string and a callback function. The callback function has one parameter of type `&str`, which represents the downloaded data. The callback function also has an `impl FnOnce` trait bound, which means it can be any type of function or closure that takes one `&str` argument and does not return anything.

Inside the `download_data` function, we simulate downloading some data by printing some text and sleeping for one second. Then we create some fake data by formatting a string with the url. Finally, we execute the callback function with the data as an argument.

We also define a closure `print_data` that takes one `&str` argument and prints some text with it. We pass this closure as an argument to the `download_data` function along with a url string.

When we run this code, we get something like this:

Downloading data from https://www.rust-lang.org...
Received data: Some data from https://www.rust-lang.org

As you can see, closures make it easy and concise to write custom logic for callbacks.

Conclusion

In this tutorial, you learned what closures are, how to create and use them, and why they are awesome. You saw how closures allow you to create anonymous functions that can capture values from their environment and adapt to different contexts. You also saw how closures can be used for tasks such as filtering data collections or implementing callbacks.

Closures are one of Rust's most powerful features that enable expressive and elegant code. They are fun and useful, but they can also be tricky and confusing at times. Don't worry if you don't get them right away; practice makes perfect! 😉

Previous
Next

Lesson discussion

Swap insights and ask questions about “Learn everything about Rust”.

Enroll to participate
Start the course to unlock the discussion. Enrolling helps us keep conversations relevant to learners.
WebsiteDiscoverPartner with UsBlogEvents
Discover
CoursesCircleRustSoliditySolanaWeb3 FundamentalsBlockchain Basics
CompanyAbout UsBrand GuidelineFAQsTerms of UsePrivacy PolicyGDPR NoticeCookies
Don't miss any update!

Disclaimer: The information, programs, and events provided on https://risein.com is strictly for upskilling and networking purposes related to the technical infrastructure of blockchain platforms. We do not provide financial or investment advice, nor do we make any representations regarding the value, profitability, or future price of any blockchain or cryptocurrency. Users are encouraged to conduct their own research and consult with licensed financial professionals before engaging in any investment activities. https://risein.com disclaims any responsibility for financial decisions made by users based on the information provided here.

© 2026 Rise In, All rights reserved