Welcome! This is my personal blog about Web technologies, software development, open source and other related topics
The ideas and opinions expressed here are solely mine and don't represent those of others, either individuals or companies.The code snippets or references to software products or analogous are to be used without any warranty of any kind. If you enjoy the content, feel free to share it and re-use it as long as you provide a link to the original post.
Variable bindings have a scope, and are constrained to live in a block. A block is a collection of statements enclosed by braces {}.
In the below example the scope for the variable language is only within the enclosed block.
if true { let language = "English"; println!("Language is {}", language); }
If this variable is accessed outside the block it will be a compiler error-
if true {
let language = "English";
println!("Language is {}", language);
}
println!("Language is {}", language);
To make the scope within the main method declare the vaiable outside the block.
let language = "English";
if true {
println!("Language is {}", language);
}
println!("Language is {}", language);
Output
Language is English
Language is English
Variable shadowing
Variable shadowing occurs when a variable declared within a certain scope (decision block, method or inner class) has the same name as a variable declared in an outer scope.
let language = "English";
if true {
println!("Language is {}", language);
}
let language = "French";
println!("Language is {}", language);
Output
Language is English
Language is French
In the above code the variable language is declared twice. Rust allows multiple variables with the same name, and these varaibles can be of different data type. The scope of the first variable remains until the next variable with same name is declared. The last variable with same name remains for the rest of the code.
Shadowing variable with different data type
let language = "English";
if true {
println!("Language is {}", language);
}
let language = 1; //here the first variable is de-scoped
println!("Language is {}", language);
Output
Language is English
Language is 1
When used correctly shadowing can be used for reusing common variable for different purposes and the ability to the mutability of data type. If this is not done carefully this can have a strange bugs in code.
Variables must be explicitly declared as mutable using mut keyword
Rust is a statically typed language – all variable data types must be know at compile time.
Example-
//default immutable. value cannot be changed after declaration
let x = 10;
// marked as mutable. i.e value can be changed after declaration
let mut y = 20;
Compiler Errors-
let x = 10;
println!("x is {}", x);
x=20; // Error. Cannot be assigned twice. consider making this muttable
println!("x is {}", x);
Solution-
let mut x = 10; // make this mutable
println!("x is {}", x);
x=20; // compiles successfully
println!("x is {}", x);
Type of the varaible is declared after the : (colon)
Example:-
// signed 8-bit integer. max value can hold is 255
let x: i8 = 255;
let y: i8 = -10;
// unsigned 8-bit integer.
let z: u8 = 255;
Compiler Errors-
// unsigned 8-bit integer. max value 255
let z: u8 = 256;
Solution-
// unsigned 8-bit integer. max value 255
let z: u8 = 255; // compiles successfully
Runtime Errors-
// unsigned 8-bit integer. max value 255
let mut z: u8 = 255;
z = z + 1; // Error: panicked - attempt to add with overflow
println!("z is {}", z);
Solution-
// unsigned 8-bit integer. max value 255
let mut z: u8 = 254;
z = z + 1; // compiles and runs successfully
println!("z is {}", z);
Output- z is 255
Floating-Point Data Types
Represent numbers with decimal points
f32 and f64 are two floating-point types
Value stored as fractional and exponential components
f32- represents value from 6 to 9 decimal digits of precision.
f64- represents value from 15 to 17 decimal digits of precision.
Example-
let x= 10.00; // default is f64
println!("x is {} deafult type is f64", x);
let y: f32= 10.1234567890132456798; // default is f64
println!("y is {} with f32 floating-point type", y);
let z: f64= 10.1234567890132456798; // default is f64
println!("z is {} with f64 floating-point type", z);
Output-
x is 10 deafult type is f64
y is 10.123457 with f32 floating-point type
z is 10.123456789013245 with f64 floating-point type
Guidelines for Numeric data types–
Values with fractional component or decimal places use floating-point data type.
Use f64 or been default gives most precision range. With modern computers it is as good as using f32. So perrfomance should not be an issue.
Use f32 with embedded systems where memory is a limitation.
Default 32 signed integer (i32) provides a generous range – between and around plus or minus 2 billion
For memory concerns use smaller size integers to conserve and use less memory
Arithmetic Operations
Rust uses arithmetic operations with the operators +, -, *, / and %
Artihmentic operation results depend on the type of variables. example, if both operators are integer then will get result in integer
let x= 10; // default is i32
let y=3; // default is i32
let z= x / y;
println!("z is {}", z) // so z will be i32
Output- Here the value of z is 3 since both the variables are defaulted to signed integer i.e. i32
z is 3
Now if we add decimals to x and y the output is floating-point-
let x= 10.00; // default is f64
let y=3.00;
let z= x / y;
println!("z is {}", z)
Output- Here the default is f64.
z is 3.3333333333333335
Compiler Errors-
If one of the variables data type is different from other, then will receive error-
let x= 10; // default is i32
let y=3.00; // default is f64s
let z= x / y;
println!("z is {}", z)
Here the default integer is i32 and floating-point is f64. Dividing different data types will result in error. This is applicable for all artihmetic operations.
Solution-
For the above error cast the variable.
let x= 10; // default is i32
let y=3.00; // default is f64s
let z= x as f64 + y;
println!("z is {}", z)
Binary trait formats the output as a number in binary
Example-
let value= 0b11110101u8;
println!("value is {}", value)// Prints the number
println!("value is {:b}", value) // prints the binary
println!("value is {:010b}", value) // prints the binary upto 10 digits and preceding 0.
Output-
value is 245
value is 11110101
value is 0011110101
Here the binary is converted to number. The prefix 0b mentions the value is binary and suffix u8 converts the binary to unsigned integer.
Bitwise operations-
Logical operations on patterns using NOT, AND, OR, XOR and SHIFT.
Taking the above example, Bitwise NOT-
let mut value= 0b11110101u8;
value=!value;
println!("value is {:08b}", value); // prints the negated binary
Output-
value is 00001010
Bitwise AND- change the value of a specific Bit-
Use the & with the anotehr binary to change the value.
Example- to change the value of the highlighted 11110111 to 0, & this with 11110011. Ouput will be 11110011
Example-
let mut value= 0b11110111u8;
value=value & 0b11110011;
println!("value is {:08b}", value);
Output-
value is 11110011
Likewise use | (pipe) for OR, ^ for XOR, << Left shift and >> Right Shift
Boolean data types and operations
Use the above operatos used in bitwise for boolean operations.
Example-
let a = true;
let b= false;
println!("a is {} and b is {}", a, b);
println!("NOT a is {} ", !a);
println!("a AND b is {} ", a & b);
println!("a OR b is {} ", a | b);
println!("a XOR b is {} ", a ^ b);
Output-
a is true and b is false
NOT a is false
a AND b is false
a OR b is true
a XOR b is true
Short-Circuiting Logical Operations
With this in OR operation if the left side is True then it won’t check the right side since the result for OR will be always true.
Similarly, for AND operation if the left side is False, it won’t check the value at the right side, since the result will be always False.
This can be done using && or ||. This improves the code effeciency.
Example-
let a = true;
let b= false;
let c= (a ^ b) || (a & b);
println!("c is {}", c);
Use compareision operators to compare values. Some of these are-
Equal ==
Not Equal !=
Greater Than >
Less Than <
Greater Than Equal >=
Less Than Equal <=
Example-
let a = 1;
let b= 2;
println!("a is {} and b is {}", a, b);
println!("a EQUAL TO b is {} ", a == b);
println!("a NOT EQUAL TO b is {} ", a != b);
println!("a GREATER THAN TO b is {} ", a > b);
println!("a LESS THAN TO b is {} ", a < b);
println!("a GREATER THAN EQUAL TO b is {} ", a >= b);
println!("a LESS THAN EQUAL TO b is {} ", a <= b);
Output-
a is 1 and b is 2
a EQUAL TO b is false
a NOT EQUAL TO b is true
a GREATER THAN TO b is false
a LESS THAN TO b is true
a GREATER THAN EQUAL TO b is false
a LESS THAN EQUAL TO b is true
The comparision can be used with the similar data types.
Char Data Type
Represents a single character
Stored using 4 bytes
Unicode scalar value
Char can be represented within a single quote e.g.- ‘a’
Unicode characters can be represented using “\u{hexa decimal value}”
let letter = 'a';
let number = '1';
let unicode= '\u{0024}';
println!("Letter is {}", letter);
println!("Number is {}", number);
println!("Unicode is {}", unicode);