Datatype

A datatype is a type of value which is stored in the memory as a variable and is referred by the programming language. A data type signifies which type of value should a variable store, irrespective of the actual memory location allocated to the variable.

A datatype also defines the bit size of a variable, which means how many bits are required to store one variable in the memory location.

The importance of datatype lies in the fact, that when the same programming languages is made to run in different machines or platforms, the datatype is the one which will take care of the variable type and maintain consistency.

There are certain operations, functions, and modules which are created to work directly based on the datatypes of the variable.

For example, there are constructors which can be pre-defined with a particular datatype, now when the constructor is invoked, automatically, the constructor will work on the data which matches its datatype. This is one example, how a datatype can be important.

Primitive Data Types

bool The boolean type.
char A character type.
i8 The 8-bit signed integer type.
i16 The 32-bit signed integer type.
i32 The 64-bit signed integer type.
i64 The pointer-sized signed integer type.
isize The pointer-sized signed integer type.
u8 The 8-bit unsigned integer type.
u16 The 16-bit unsigned integer type.
u32 The 32-bit unsigned integer type.
u64 The 64-bit unsigned integer type.
usize The pointer-sized unsigned integer type.
f32 The 32-bit floating point type.
f64 The 64-bit floating point type.
array A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.
slice A dynamically-sized view into a contiguous sequence, [T].
str String slices.
tuple A finite heterogeneous sequence, (T, U, ..).

Details and declaration types of each of the data types:

bool

A standard boolean data type. It can be either true or false.

let t = true;
let f = false;
char

It is a 4-byte character.

let a = 'a';
let b = 'b';
let keyboard = '⌨';
integer

The integer data types include i8, i16, i32, i64, isize, u8, u16, u32, u64 , usize. Here, i means signed and u means unsigned. isize and usize depend on the word size of the machine, that is on the architecture of the machine.

let a = 5;
let count = 42;
let mobile = 8675309;
floating point

It includes f32 and f 64.

let pi = 3.14;
let e = 2.718;
array

An array is a fixed sized collection of same type elements. It is declared in the below format:

let name: [type, size] = [attribute1, attribute2, attribute3, attribute4.. etc];
let array: [i32; 5] = [0, 1, 2, 3, 4];

println!("The first element of the array is: {}", array[0]);

let counter = 0;
for x in array.iter(){
    println!("The element at index {} is {}", counter, x);
    counter += 1;
}
slice

A slice is a dynamically sized collection, which is addressed in a contagious memory location.

let array: [i32; 5] = [0, 1, 2, 3, 4];

let slice = &array[0..3]; 

for x in slice {
	println!("x is {}", x):
}

In the above declaration, the slice will return 0 to 2. The first index will be inclusive and the second index will be exclusive.

str

A str is the string slice and is the most primitive data types.

let str = "Welcome to your first Rust string";
tuple

Tupes are finite heterogenous, sequences. Tuples have a fixed size and a fixed number of elements. In the array, it contains data of the same type, but in the tuple there can we have different kinds of data types.

let tuple = ("hello world", 42, "world", [3,6,9]);

println!("First element is {}", tuple.0);
println!("Second element is {}", tuple.1);
println!("Third element is {}", tuple.2);
let mut counter = 0;
for x in &tuple.3 {
    println!("Element {} of the fourth element is {}", counter, x);
    counter += 1;
}

Variables and Mutability

By default, the variables are immutable in Rust, which means once the variable is assigned one value, it cannot be assigned another or different value in the same program. If we want to do that, then we have to mute the first assigned variable and then the same variable can be assigned again with a different value.

Consider the program below:

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

In the program above, we have seen that the x is assigned value 5 and again x is assigned 6. Now, the compiler will create an error, because of the same variable x is assigned twice.

Now to overcome this error, we can use the keyword mut to mute the variable and then the same variable can be used again. Let us see how it can be done in the program below:

fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

Variables Vs Constants

Variables are the ones whose value can be changed. In Rust, of course, the value of the variable cannot be changed, as in default it's immutable, but we can mute it and change the value.

On the other hand, in the in case of the constants, the value assigned to it, remains the same through the program and by no means, the value can be changed.

A constant in Rust can be declared in this way:

const VALUE: U16= 500;

It may be noted that the immutable variables and constants may look similar. But when we are concerned about the scope, then variables are valid only in their scope, that is where it has been declared. On the other hand, the constants are global and can be accessed from anywhere inside the program.

Shadows

Shadowing a variable means creating a new variable with the same name as the existing variable. We can do this using the keyword let. Usually, the variable in the same name cannot be assigned a different value again, unless we mute it. But shadowing is a different concept.

Here, we are creating the same variable name again, but with a totally new data type and different memory location. For this to happen, every time, we have to use the keyword let, Let us consider the example below:

fn main() {
    let x = 5;

    let x = x + 1;

    let x = x * 2;

    println!("The value of x is: {}", x);
}

The output of the program is 12.

The idea behind is, the program first binds x to a value 5.

Now in the second line let x= x+1; , the x after the assignment operator is the original value, that is containing 5. And the x after the let, which is in the left-hand side if the assignment operator, is a brand-new variable created with the same name.

So, now the new variable x, stores the old value of x plus 1. It seems the same value has been updated, but actually, only its shadow is updated. So, in this way, the program output is 12.

This is called Shadowing.

Let us see another example of shadowing. Consider the program below:

let spaces = "   ";
let spaces = spaces.len();

In this program, we have seen that the first variable called spaces consists a string datatype and the second variable whose name is also spaces now contains the number of spaces as it is concatenated with the function called len().

Therefore, it is clear, that not only the variable can be used twice, but also the same variable's data type can be changed using the concept of shadowing.

Comments in Rust

There is not much to be discussed in this section. Comments are those lines or statements inside the program which are ignored by the compiler and does not have any effect on the execution of the program.

Comments can be put inside the program, just by adding // (two forward slashes) after the program statement and can be left without an ending tag on the same line. The compiler considers it as a command.

fn main() {
    println!("Hello, world!");    // This line prints the string inside the double inverted comma. This is called comment.

    my_function();        
}

my_function() {       
    println!("Hello Chercher.tech !");
}

A Sample Program in Rust

Let us consider a simple program in Rust to Add two integer number. The analysis of this program will make it clear how to declare a variable, how to take an input and so on.

Let's get started!

use std::io::{self, Write};
use std::fmt::Display;
use std::process;

fn main() {
       println!("\n");
       println!("\tAddition of Two Numbers in Rust");
       println!("\n");

    let value1: i32 = grab_input("Enter First Number ")
        .unwrap_or_else(|e| exit_err(&e, e.raw_os_error().unwrap_or(-1)))
        .trim()
        .parse()
        .unwrap_or_else(|e| exit_err(&e, 2));

    let value2: i32 = grab_input("Enter Second Number ")
        .unwrap_or_else(|e| exit_err(&e, e.raw_os_error().unwrap_or(-1)))
        .trim()
        .parse()
        .unwrap_or_else(|e| exit_err(&e, 2));


    let sum = value1 + value2;
     
    println!("\n"); 
    println!("The sum of {} and {} is {}. ",value1,value2,sum);
    println!("\n"); 
    println!("End of Program");
}

fn grab_input(msg: &str) -> io::Result<String> {
    let mut buf = String::new();
    print!("{}: ", msg);
    try!(io::stdout().flush());

    try!(io::stdin().read_line(&mut buf));
    Ok(buf)
}

fn exit_err<T: Display>(msg: T, code: i32) -> ! {
    let _ = writeln!(&mut io::stderr(), "Error: {}", msg);
    process::exit(code)
}
Note : Please don't be afraid, because the program looks complicated! We shall make it simpler for you!

Let us break the program into smaller parts and explain the function of each line. After this, Rust will seem to you, like any other typical programming languages.

use std::io::{self, Write};

This statement says that, the standard input and output function must be called at the time of execution of the program. It means explicitly use the std::io::Write and also use std::io itself.

use std::fmt::Display;

This statement means that it should use the standard format display during the program execution. Fmt means Format.

use std::process;

This is a module which is used for spawning and interacting with the child process. It is also used to aborting and exiting from the current process execution.

fn main() {

fn main() is the main function, where the program execution starts. And the { (open brace) shows that the scope of the main function has started.

println!("\n");

println!() is the standard inbuilt function from the Rust library which is used to print data when put inside as its parameter.

It is similar to the cout function in C++.

println!("\tAddition of Two Numbers in Rust");

This statement will print the string inside the double inverted comma, following a space of tab size (as \t is used).

println!("\n");

This statement will create a new line.

let value1: i32 = grab_input("Enter First Number ");

This declares a value called value1 of i32 datatype and assigns it to the return value of the function grab_input().

Therefore, while executing, the grab_input function(user-built) will display the text inside it, and prompts the user for a data. That data will be assigned to value1.

.unwrap_or_else(|e| exit_err(&e, e.raw_os_error().unwrap_or(-1)))

This statement handles error in an standard way, we shall see later in detail in our Error Handling Section in Rust.

.trim()

This function is used to remove white spaces from both the sides of a string or any input.

.parse()

This function is called to apply rules of syntactic analysis, by the compiler.

.unwrap_or_else(|e| exit_err(&e, 2));

This statement is for handling error and exiting if an error is encounterred.

let value2: i32 = grab_input("Enter Second Number ")

This statement declares a value called value2 if i32 datatype and the return value of the grab_input() function is assigned to it. This will prompt the user to enter an input in the time of execution.

.unwrap_or_else(|e| exit_err(&e, e.raw_os_error().unwrap_or(-1)))

This is again error handling.

.trim()

This function is used for removing white spaces.

.parse()

To parse the string.

.unwrap_or_else(|e| exit_err(&e, 2));

Error Handling.

let sum = value1 + value2;

This statement declares a new variable sum and the summation of value1 and value2 is assigned to it.

println!("The sum of {} and {} is {}. ",value1,value2,sum);

This statement prints the value sum .

println!("\n");

This statement prints a new line.

println!("End of Program");

This statement prints a string.

}

This is a closing brace, which closes the scope of the main function.

fn grab_input(msg: &str) -> io::Result<String> {

This statement declares a function grab_input .

let mut buf = String::new();

editing......





Comment / Suggestion Section
Point our Mistakes and Post Your Suggestions