What are closures in Rust?

Overview

Anonymous functions are known as closures in Rust. We can store them in a variable or a function takes them as a parameter. They behave like a function, but they possess the property of capturing values from the environment in which we define them.

Syntax

Let’s look at the syntax of closures below:

let variable_name = |list of parameters| -> return_type {"body of the closure"};
  • let: This is used to declare any variable in rust.

  • variable_name: This is the name of the variable we assign the closure to.

  • list of parameters: This is the list of parameters we pass to the closure, which is enclosed in between | |.

  • return_type: This contains the return type of values we want to return.

  • {}: This contains the body of the closure.

Difference between closures and functions

Property

Closure

Function

Syntax

Parameters passed between ||

Parameters passed between ()

Body

Enclosed between {} in case of multiple statements. In the case of a single statement, not mandatory.

Mandatory to enclose between {} in the case of every statement.

Return types

Not mandatory

Mandatory

Data types

Not mandatory

Mandatory

Environment capturing

Yes

No

Code example

We can call closures in the same way as functions, as shown below:

fn main(){
let message = " Welcome to educative ";
let print_message = || { println!("Message is : {}", message); };
print_message();
}

Code explanation

The program uses a closure to print a message. It also demonstrates the environment capturing property of closure.

  • Line 2: We declare a variable to store the message as a string. Here, we did not explicitly mention the type of the variable, so it is inferred as a string.
  • Line 3: We define a closure print_message with no input parameters. The body of the closure contains a print statement that prints the value of the variable message defined out of this closure. This is the environment capturing property of closures.

Call traits in closures

All closures implement the fnOnce trait. With the help of this property, we make sure that we call a closure only once.

A closure can implement a few other call traits depending on how it captures its environment.

  • Closures can implement fnmut, which means we can call them using a mutable reference. This is applicable where the closure has to borrow a variable.
  • A closure that does not need to borrow a variable in a mutable manner uses fn.

The move keyword in closures

Whenever a closure uses a variable from its capturing environment, it captures either by reference or by mutable reference. The move keyword helps capture the closure’s environment by value. It gives them the ownership of the variable to the closure.

Code example

Let’s look at the code below:

fn main() {
let prime_numbers = vec![2, 3, 5,7];
let closure_variable = move |vec| vec == prime_numbers;
println!("Cannot use prime_numbers here : {:?}", prime_numbers);
let new_vector = vec![2, 3, 5,7];
assert!(closure_variable(new_vector));
}

Code explanation

The above code throws an error:

  • Line 3: The closure closure_variable takes the ownership of the variable prime_numbers using a move keyword.
  • Line 4: The println! function borrows the prime_numbers variable to print, but the compiler throws an error, as the closure now owns the variable, and the move keyword moves it in the closure.
Copyright ©2024 Educative, Inc. All rights reserved