You are on page 1of 2

# Common Programming Concepts

## Variables and Mutability

- all variables are immutable by default


- use `mut` to make it mutable
- `let mut a = 5;`
- `let mut b = String::new();`
- Use `mut` in cases with large data structures because mutating an instance in
place may be faster than copying and returning newly allocated instances
- can't assign a value of a different type
```rust
let mut spaces = " ";
spaces = spaces.len(); // throws error
```

> function that follows `::` is called an associated function of the class before
`::`.

### Constants
differs from immutable variables:
- can't use `mut` with `const`
- const are always immutable
- the type of const MUST be annotated
- set to constant expression (can't be calculated at runtime depending on other
vars. can do primitive calcs like `60*3`)
- can be declared in any scope (incl global)

Naming convention: `THREE_HOURS_IN_SECONDS`

### Shadowing

Can create var with the same name, so the program will see the new value. In that
case they say the old value was shadowed by the new.

By shadowing we can change the type of variable as well to avoid having names
`spaces_string`, `spaces_num`. But when trying to use mutable variable assigning to
the same var different types would throw and error.

```rust
fn main() {
let x = 5;

let x = x + 1;

{
let x = x * 2;
println!("The value of x in the inner scope is: {}", x); // 12
}

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


}
```

```rust
let mut spaces = " ";
spaces = spaces.len(); // expected `&str`, found `usize`
// not allowed to mutate a variable’s type
```
## Data Types

Every value has a type. Rust is `statically typed` = it must know the types of all
variables at compile time. The compiler guesses the type, but in cases of ambiguity
it requires type annotation.

Two data type subsets:


- scalar
- integer
- signed (i8, i16, `i32`, i64, i128, isize)
- stores nums from -2^(n-1) to 2^(n-1)-1
- unsigned (u8, u16, u32, u64, u128, usize)
- stores nums from 0 to 2^n-1
- isize and usize depend on the computer architecture 64 or 32 bits. Use it
when indexing some sort of collection.
- i32 is the default, so use it when unsure which int to use.
- if integer overflow happens:
- in debug mode Rust would `panic` which means it would exit with an
error
- in release mode Rust would perform `two's complement wrapping`, e.g.
in u8 256 becomes 0, 257 becomes 1 - not what we want
- `check how to handle potential overflows in docs`
- floating-point number
- f32 and `f64` - default
- boolean
- one byte size `bool`, values `true` or `false`
- characters
- four byte `char`
- specified with single quotes
- primitive compound
- tuple
- e.g. `let tup: (i32, f64, u8) = (500, 6.4, 1);`
- can store several types
- fixed length - once declared can't grow/shrink
- destructure by `let (x, y, z) = tup;`
- access elements by `let first_elem = tup.0;`
- tuple without any value `()` is a special type called `unit type` and the
value is `unit value`. Unit value is returned if there is no any other value. Used
as a None.
- array
- e.g.
- `let a: [i32; 5] = [1, 2, 3, 4, 5];`
- `let a = [3; 5];` - repeat value `3` 5 times
- stores only one type
- fixed length
- use when the number of elems doesn't change. In other cases use vector
- when trying to access a non-existing index, it would first check the
correctness of index - that's the memory safety that Rust provides. It won't access
invalid memory.

You might also like