Rust variables

From wikinotes
Revision as of 23:06, 7 February 2023 by Will (talk | contribs) (→‎Declaration)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Declaration

You may declare variables in rust, but it is not necessary.
It is illegal to use a variable before it has been bound to a value (null does not exist in rust).

let foo: u8;

Assignment

let age: u8 = 200;   // typed
let age = 200u8;     // type-suffix
let age = 200;       // implied type
let age: i32 = age;  // type cast from 'u8' to 'i32'

// conditional assignment
let weather = if season == "fall" { "lovely" } else { "fine I guess" }

// loop assignment
let result = loop { break 123 }  // break can be passed a return val

// conditional on assignment (match-option, that only handles success)
if let Some(val) = my_val {
    println!("my val is {}", val);
}

Type Casting

When type fits into another (ex. u8 to u32).

let age: u8 = 30;
let age: u32 = age;  // type cast from 'u8' to 'i32'

u64::from(30i8);     // type cast from 'i8' to 'u64'

When type does not fit into another (ex. i32 to i8).

use std::convert::TryFrom;

i8::try_from(100i32).ok();  // try cast from 'i32' to 'u8' (result type)

Literals

Literal types can be declared without assigning a type.

let float = 3.14;  // f64
let integer = 7;  // i32

Constants

Constants can be declared in any scope (including global).
constants cannot be changed once assigned.
memory addresses for the same const will not be the same (const is inlined where used).

const SALT: &str = "$6$r1ohStL5/UwpNnls";  // inlined where used
static RETRIES: i8 = 5;                    // not-inlined where used (same mem-addr)

Scope

Variable scope is bound to the block they are defined in { ... }.
Blocks may be defined arbitrarily to create inner scopes.
Variables defined in outer scopes are accessible in inner scopes.

fn foo() {
  let foo = 1;
  {
    println!("{}", foo);
  }
}

Access Control

Everything is private in rust by default, unless explicitly declared otherwise.
Elements are declared public with the pub modifier.

fn foo() { ... }      // private function
pub fn foo() { ... }  // public function

Mutability and Freezing

Mutability

All variables are immutable by default in rust.
You can make them mutable with the mut modifier.

let mut age: i8 = 30;
age += 1;

Freezing

You can bind mutable variables as immutable within inner scopes.
This will prevent them from being modified within that scope.
This is known as freezing a variable.

let mut age: i8 = 30;

{
  let age = age; // immutable until block scope ends
}

Shadowing

You can shadow immutable variables, the result is effectively a new variable.

// valid!
let height = 2; println!("{height}");
let height = 3; println!("{height}");

Shadowing/Type Conversion

let num = "32";
let num: i8 = num.parse().unwrap();  // converts 'str' to 'i8'

Copy/Cloning Values

Stack Allocated Copies (Shallow)

Fixed-size variables allocated to the stack can implement the Copy trait.
This allows you to shallow copy objects through assignment.

let x = 5;
let mut y = x;
y += 1;
println!("x = {}, y = {}", x, y);
// x = 5, y = 6

Heap Allocated Copies (Deep)

// copy pointer
let foo_orig = String::from("foo");
let foo_copy = foo_orig;

// deep copy
let foo_orig = String::from("foo");
let foo_copy = foo_orig.clone();     // new allocation in heap

Introspection

Easiest way is to call a method on it that doesn't exist.
the stacktrace will have the type.

let foo = 123;
foo.cuss();  // <-- invalid method