Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. In today’s tutorial we are focusing on equipping you with recipes to programming with Rust and also help you define expressions, constants, and variable bindings. Let us get started:
An expression, in simple words, is a statement in Rust by using which we can create logic and workflows in the program and applications. We will deep dive into understanding expressions and blocks in Rust.
We will require the Rust compiler and any text editor for coding.
Follow the ensuing steps:
// main point of execution fn main() {
// expression
let x_val = 5u32;
// y block
let y_val = {
let x_squared = x_val * x_val;
let x_cube = x_squared * x_val;
// This expression will be assigned to `y_val`
x_cube + x_squared + x_val
};
// z block
let z_val = {
// The semicolon suppresses this expression and `()` is assigned to `z`
2 * x_val;
};
// printing the final outcomes println!("x is {:?}", x_val); println!("y is {:?}", y_val); println!("z is {:?}", z_val);
}
You should get the ensuing output upon running the code. Please refer to the following screenshot:
All the statements that end in a semicolon (;) are expressions. A block is a statement that has a set of statements and variables inside the {} scope. The last statement of a block is the value that will be assigned to the variable. When we close the last statement with a semicolon, it returns () to the variable.
In the preceding recipe, the first statement which is a variable named x_val , is assigned to the value 5. Second, y_val is a block that performs certain operations on the variable x_val and a few more variables, which are x_squared and x_cube that contain the squared and cubic values of the variable x_val , respectively. The variables x_squared and x_cube , will be deleted soon after the scope of the block.
The block where we declare the z_val variable has a semicolon at the last statement which assigns it to the value of (), suppressing the expression. We print out all the values in the end.
We print all the declared variables values in the end.
Rust provides the ability to assign and maintain constant values across the code in Rust. These values are very useful when we want to maintain a global count, such as a timer-- threshold--for example. Rust provides two const keywords to perform this activity. You will learn how to deliver constant values globally in this recipe.
We will require the Rust compiler and any text editor for coding.
Follow these steps:
// Global variables are declared outside scopes of other
function
const UPPERLIMIT: i32 = 12;
// function to check if bunber fn is_big(n: i32) -> bool {
// Access constant in some function
n > UPPERLIMIT
}
fn main() {
let random_number = 15;
// Access constant in the main thread println!("The threshold is {}", UPPERLIMIT); println!("{} is {}", random_number, if is_big(random_number) { "big" } else { "small"
});
// Error! Cannot modify a `const`.
// UPPERLIMIT = 5;
}
You should get the following screenshot as output upon running the preceding code:
The workflow of the recipe is fairly simple, where we have a function to check whether an integer is greater than a fixed threshold or not. The UPPERLIMIT variable defines the fixed threshold for the function, which is a constant whose value will not change in the code and is accessible throughout the program.
We assigned 15 to random_number and passed it via is_big (integer value); and we then get a boolean output, either true or false, as the return type of the function is a bool type. The answer to our situation is false as 15 is not bigger than 12, which the UPPERLIMIT value set as the constant. We performed this condition checking using the if...else statement in Rust.
We cannot change the UPPERLIMIT value; when attempted, it will throw an error, which is commented in the code section.
Constants declare constant values. They represent a value, not a memory address: type = value;
Variable binding refers to how a variable in the Rust code is bound to a type. We will cover pattern, mutability, scope, and shadow concepts in this recipe.
We will require the Rust compiler and any text editor for coding.
Perform the following step:
fn main() {
// Simplest variable binding let a = 5;
// pattern
let (b, c) = (1, 2);
// type annotation let x_val: i32 = 5;
// shadow example let y_val: i32 = 8;
{
println!("Value assigned when entering the scope : {}", y_val); // Prints "8".
let y_val = 12;
println!("Value modified within scope :{}", y_val);
// Prints "12".
}
println!("Value which was assigned first : {}", y_val);
// Prints "8". let y_val = 42;
println!("New value assigned : {}", y_val);
//Prints "42".
}
You should get the following screenshot as output upon running the preceding code:
The let statement is the simplest way to create a binding, where we bind a variable to a value, which is the case with variable a. To create a pattern with the let statement, we assign the pattern values to b and c values in the same pattern. Rust is a statically typed language. This means that we have to specify our types during an assignment, and at compile time, it is checked to see if it is compatible. Rust also has the type reference feature that identifies the variable type automatically at compile time. The variable_name : type is the format we use to explicitly mention the type in Rust. We read the assignment in the following format:
x_val is a binding with the type i32 and the value 5.
Here, we declared x_val as a 32-bit signed integer. However, Rust has many different primitive integer types that begin with i for signed integers and u for unsigned integers, and the possible integer sizes are 8, 16, 32, and 64 bits.
Variable bindings have a scope that makes the variable alive only in the scope. Once it goes out of the scope, the resources are freed.
A block is a collection of statements enclosed by {}. Function definitions are also blocks! We use a block to illustrate the feature in Rust that allows variable bindings to be shadowed. This means that a later variable binding can be done with the same name, which in our case is y_val. This goes through a series of value changes, as a new binding that is currently in scope overrides the previous binding. Shadowing enables us to rebind a name to a value of a different type. This is the reason why we are able to assign new values to the immutable y_val variable in and out of the block.
[box type="shadow" class="" width=""]This article is an extract taken from Rust Cookbook written by Vigneshwer Dhinakaran. You will find more than 80 practical recipes written in Rust that will allow you to use the code samples right away in your existing applications.[/box]
20 ways to describe programming in 5 words
Top 5 programming languages for crunching Big Data effectively