Common Programming Concepts in Rust
Variables and mutability
Variables are immutable by default. I said it.
fn main() {
let x = 5;
println!("The value of x is {}", x);
x = 42;
println!("The value of x is {}", x);
}
x
.
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
3 | println!("The value of x is {}", x);
4 | x = 42;
| ^^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `playground` due to previous error
Make it mutable as we learned:
Constant
Values that never changes, create it by using const
keyword (make sure to provide a type as well, it's required):
So, why do we require constants, when we already have immutable variables?
Constants v/s variables
- We cannot mutate a constant.
mut
keyword is invalid withconst
. const
s must also be type annotated.const
can also be set to constant expressions. We cannot assign return value of functions to them.
Shadowing
Shadowing allows us to create new variables using an existing name.
The below code is valid:
!!! "Advantages of shadowing"1. We can preserve immutability.
Both `x` in above example remains immutable.
2. We can change type.
```rust
let x = 5;
let x = "six";
```
Data Types
We have two types of Data Types: 1. Scalar data types: represent a single value 2. Compound data types: represent a group of values
Scalar data types:
- Integers: Numbers without a fractional component
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 |
u8 |
16-bit | i16 |
u16 |
32-bit | i32 |
u32 |
64-bit | i64 |
u64 |
128-bit | i128 |
u128 |
arch (architecture based) | isize |
usize |
Signed integers can be positive or negative, unsigned are positive only. By default rust uses signed 32 bit integer.
fn main() {
let a = 98_222; // Decimal
let b = 0xff; // Hex
let c = 0o77; // Octal
let d = 0b1111_0000; // Binary
let e = b'A'; // Byte (u8 only)
// Any value greater than what it can hold will
// in Debug builds will panic
// in Release builds will wrap around back to minimum
// i.e., perform 2s Complement wrapping
let f: u8 = 255;
}
- Floating point numbers
- Booleans
- Characters: represent unicode characters
Compound Data Types
- Tuples: A tuple is a collection of values of different types constructed using paranthesis.
fn main() {
let tup = ("Hello, World!", 100_000);
// To get values of tuple
// 1. Destructuring
let (str_var, int_var) = tup;
// 2. Dot notation
// tuples and array start at index 0
let int_var = tup.1;
}
- Arrays: An array is a fixed length collection of objects of the same type T, stored in contiguous memory, constructed using bracked [].
fn main() {
let http_err_codes = [200, 404, 500];
let res_not_found = error_codes[1];
// create array of size 8, all set to 0
let byes = [0; 8];
}
Functions
Function naming scheme specifies that functions should named in snake case, all in lower case characters.
fn main() {
my_function(11, 22);
}
fn my_function(x:i32, y:i32) {
println!("Value of x: {}", x);
println!("Value of y: {}", y);
}
In Rust, we can think of any piece of code as either a statement or an expression.
- Statements perform some action but do not return a value
- Expressions returns a value
We can return values in two ways:
1. either using return
keyword and specifying return types of function at the end of decalaration.
fn my_function(x:i32, y:i32) -> i32 {
println!("Value of x: {}", x);
println!("Value of y: {}", y);
let sum = x + y;
return sum
}
- or inside a function the last expression is implicityly returned. So something like this is also ok:
fn my_function(x:i32, y:i32) -> i32 {
println!("Value of x: {}", x);
println!("Value of y: {}", y);
x + y
}
Control Flow
if-else statements
fn main() {
let number = 5;
// conditions needs to be explicitly stated
if number < 10 {
println!("smaller than 10");
} else if number < 22 {
println!("smaller than 22");
} else {
println!("Condition was false");
}
}
We can also use if-else
statement inside of let
statement.
Loops
This code below can loop infinitely unless we call break
.
We can also return values from these loops:
fn main() {
let mut conter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter;
}
};
println!("The result is {}", result);
}
While loop
fn main() {
let mut number = 3;
while number != 0 {
println!("{}", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
For loop
fn main() {
let a = [10, 20, 30, 40, 50];
// for every element in `a` without giving
// ownership to for loop.
// we'll learn more about Ownership later on
for element in a.iter() {
println!("the value is: {}", element);
}
// loop over range, exclusive
for number in 1..4 {
println!("Range element: {}", number);
}
}
Comments
You usually comment in Rust in either of the below two ways: