Obsidian/Zettelkasten/Permanent Notes/Programming/Rust/Chapter 3 - Common Programming Concepts.md

190 lines
5.7 KiB
Markdown

#Rust
The quick brown fox jumps over the lazy dog. The dog stays blissfully asleep. :)
# What is Chapter 3?
Chapter three is about common programming concepts that I would be familiar with
from other languages. This chapter covers variables, mutability, data types,
functions, comments, and control flow.
# 3.1 Variables and Mutability
## Variables
Rust defines variables kind of like C does:
```rust
let x = 7; // x is 7!
```
but Rust also allows explicit declaration of types when defining a variable:
```rust
let x: u32 = 7; // x is a 32 bit unsigned integer with value 7
let y: f32 = 7.0; // y is a 32 bit float with value of 7.0
```
Variables can be defined in specific scopes that do not escape the inner scope:
```rust
let x: u32 = 4;
{
let mut x = x;
x += 2;
}
println!("{x}")
>> 4 // NOT 6.
```
## Mutability
All variables in Rust are immutable unless specifically mentioned. This is
part of ensuring memory safety--you will not be able to overwrite variables
unless you declare that they can change over time. Here's an example:
```rust
let x: u32 = 2;
x += 2; // Will fail. x is not mutable
let mut y: u32 = 2;
y += 2; // Will work, since y is mutable
```
### Constants
Constants are a special case in Rust. They are immutable variables just like
`let`, but they have a special ability to be defined in the global scope,
where `let` may not be. Here's an example.
```rust
const TWO_PLUS_THREE: u32 = 2 + 3;
fn main() {
println!("{TWO_PLUS_THREE}");
}
>> 5
```
Constants are by convention written in UPPER_CAMEL_CASE.
# 3.2 Data Types
Rust is a statically typed language. Variables in Rust must have their types
known at compile time, or else the compilation will fail. A lot of times types
can be inferred, but this is not possible for cases where there are multiple
possible types.
For these cases, the type must be annotated, like this:
```rust
let annotated: str = "This one is annotated!";
let not_annotated = "This one is not!";
```
## Scalar Types
Scalar types are those that only take on one object. Examples in Rust are
integers, characters, booleans, and floating-point numbers.
Integers can be signed or unsigned, with different levels of bit resolution,
up to 128 bits. That being said, there is an 'arch' length that is the size of
the operating system. This is used with `isize` and `usize`. Signed integers are
stored with two's complement.
Numbers can also be written in differnet formats, including decimals (with _
escapes), hex values, octal values, binary values, or even as a byte(b'A').
>[!note] A special note about *integer overflow*.
> Rust code compiled and ran in debug mode will have checks for integer
> overflow, causing the code to panic at runtime if an overflow occurs.
> Production code compiled with the `--release` flag will NOT panic however,
> but instead will wrap around to the first value in the possible range.
# 3.3 Functions
Rust functions have a couple of interesting properties:
1. Rust functions don't care about order. They can be in any place in the code,
as long as the scope of things is maintained relative to where calls happen.
2. Functions start with `fn`, followed by a name in snake case, then inputs,
then the function scope using {}.
*functions* is an example of some basic functions
## Parameters
Parameters can be used to pass values into functions. When this is done, two
things must happen: First, the value must be input into the function (duh!).
Second, the function must *match the type defined in the function*.
Arguments are a similar thing to parameters, but have a technical difference
that arguments are concrete values. Like add(5), vs add(x).
## Statements and Expressions
A *statement* is a line of code that does some computation or other calculation,
and does NOT return a value.
A *expression* evaluates to a resultant value.
Rust does something interesting. When defining a variable, the output is a
*statement*, not an expression. Using `let` to define the variable does NOT
return the variable itself. Defining functions are also statements.
Calling functions, macros, or using a scope block created with curly brackets
is an expression however. Here's a mind bending example:
```Rust
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {y}");
}
```
This is an statement (defining main), with a statement inside (defining y) which
has an expression inside (the statement defining x, and then the expression adding x).
>[!Important] Statements, Expressions, and Semicolons
> Notice `x+1` does **not** end in a semicolon. If it did, it would be a statement.
> Expressions do not end in semicolons.
## Functions with Return Values
Functions can have outputs, but they do not need to be defined with `return` keywords.
Instead, the function will by default return the last expressions result.
Functions must declare the type of the output:
```Rust
fn five() -> i32{
5
}
```
# 3.4 Comments
This one is pretty simple.
```Rust
// This is a single line comment.
let x = 22; // They can be after code
/* Or, if I've really got something to say
I can use this multiple line comment */
```
# 3.5 Control Flow
## `if` Statements
If statements in Rust are expressions, that optionally have else and else if
statements.
```Rust
if number > 5 {
println!("Yay!")
} else if number < 4{
println!("Boo!")
} else {
println!("Just right!")
}
```
Conditions for if statements *must* return Boolean types.
## Repetition with Loops
To do loops in Rust, there are three main keywords for loops: `loop`, `while`, and
`for`. `loop`s will loop infinitely until a `break` statement is triggered,
`while` operates while a condition is true, and `for` loops through a specific
number of iterations.