Rust datatypes
Rust does not use null, instead is uses Option
and Result
.
Documentation
primitives https://doc.rust-lang.org/std/#primitives
Literals
General
'a' // char "abc" // string (immutable) 1234 // i32 3.14 // f32 true/false // boolNumeric Representations
1_000 // == 1000 1.000_000 // == 1.000000 0xff // hex Oo644 // octal 0b1111_0000 // binary b'A' // byte (u8)Type Suffixes
1u8 // '1' as a u8 1i64 // '1' as a i64
Primitives
Text
str
- immutable
- size-known at compile-time
https://doc.rust-lang.org/std/primitive.str.html
let name: &str = "vaderd"; // assign string let mut user = String::new(); // utf8 string // type casting '&str' to 'i8' let num: i8 = "123" .parse() .unwrap(); "foo".to_string() // type cast '&str' to 'String' (heap)Some Useful Methods
"abc".len() // 3 number of bytes used "abc".ends_with("bc") // true if ends with // bytes-array to &str let hello_bytes: [u8; 5] = [72, 69, 76, 76, 79]; let hello = std::str::from_utf8(&hello_bytes[..]).unwrap(); // 'HELLO'char
chars refer to a single character, and it's literals use single-quotes.
chars use 4-bytes in memory; they can store multibyte characters.https://doc.rust-lang.org/std/primitive.char.html
let foo: char = 'a';String
String types are strings with a string-size that is unknown at compile-time.
let mut my_str = String::from("foo") let mut my_str = "foo".to_string() my_str.as_str(); // get string-slice from string (reference) // string concatenation my_str.push_str("bar"); // foobar (push &str) my_str.push('!') // foobar! (push char) let s1 = String::from("foo") let s2 = String::from("bar") let s3 = s1 + &s2 // 's1' dropped, b/c ownership changes let s3 = format!("{s1}{s2}") // 's1' not dropped, b/c params are referencespartial strings, and representation. See representation types
to final representation applying diacritics is not in standard library.// utf-8 has multibyte `scalar` characters // `scalar` characters may be `diacritics`, // intended as modifiers for characters that follow. let s: &str = &name[0..4] // str from first 4x bytes (panic if not full unicode glyph) "123".chars() // enumerable of `scalar` chars (incl. diacritics) "123".bytes() // enumerable of `bytes` for charsNumbers
implied type let var = 12;
assigned type let var: i8 = 12;
type suffix let var = 12i8;
int
- signed integers range is split in two, can be positive/negative
- unsigned integers are positive, and use all available bits
- use radix to calculate max size that can be accomodated with b bits
// signed integers, by bit-size i8 // -128..127 i16 // -32768..32767 i32 // -2147483648..2147483647 i64 // ... i128 isize // your CPU wordsize (ex. i32 or i64) // unsigned integers, by bit-size u8 // 0..255 u16 // 0..65535 u36 // 0..4294967295 u64 // ... u128 usize // your CPU wordsize (ex. u32 or u64)float
f32 f64bool
true false fn foo(b: bool) { ... }
Collections
tuples
- heterogenous
- non-resizable
- not stored in contiguous memory
- support nesting
https://doc.rust-lang.org/std/primitive.tuple.html#
let var: (i8, char, u32) = (5, 'a', 300); var = (1, "two", 3.14) var.0 // item at index 1arrays
- homogenous
- non-resizable
- stored in contiguous memory
https://doc.rust-lang.org/std/primitive.array.html
// initialization let var: [i32; 4] = [1, 2, 3, 4]; // declare an array of 4x 32-bit integers let var: [i32; 4] = [100; 4]; // initialize all 4x ints as 100 // methods var[0] // 1 var.len() // 4 // slices let foo = &var[1..2]; // [2, 3] println!("{}", foo[0]); // 2slices
slices are a subsection of an array
let var: [i8; 4] = [1, 2, 3, 4]; let first_two = &var[0..1]; // [1, 2] let first_two = &var[..1]; // [1, 2] println!("{}", foo[0]); // 2 fn foo(s: &[i32]) { ... } // borrows slice of an i32 array fn foo(s: String) -> &str { ... } // returns slice of String fn foo(nums: &[usize; 10]) -> &[usize] // returns slice of array (slice indexes always usize)vectors
vectors are essentially resizable arrays.
- homogenous
- resizable
- stored in contiguous memory
https://doc.rust-lang.org/std/vec/index.html
let v: Vec<i32> = Vec::new(); let v = vec![1, 2, 3, 4, 5]; v[0] // get value at index 0 (and panic if index is invalid) v.get(0) // return an Option (don't panic if index is invalid) v.push(34) // append to vectorstructs
Structs, along with each of their fields, are private by default.
To access outside of their module structs/fields must be defined with thepub
keyword.Regular Structs
struct Point { x: u8, y: u8 } let p: Point = Point { x: 5, y: 10 }; // assignment println!("point({}, {})", p.x, p.y); // access fields with '.'Structs have syntactic sugar so that you can reuse parameters for assignment.
struct Coord { x: u8, y: u8, z: u8 }; fn build_coord_at_x0(x: u8, y: u8) { Coord{ x: 0, y, z } // bind `y`, `z` values from matching params }There is also syntactic sugar for creating a struct from another of the same type,
only replacing select values.struct Coord { x: u8, y: u8, z: u8 }; let p1 = Coord{x: 1, y: 2, z: 3}; let p2 = Coord{x: 5, ..p1}; // reuse fields from `p1` for all values except `x`Tuple Structs
struct Color(i8, i8, i8); // declaration let c: Color = Color(100, 150, 200); // instantiation c.1 = 200; // assignment (uses `.` index)Unit Structs
Unit structs have no value, they're just a type.
struct Centimeters; let cm = Centimeters;General
TODO:
not sure this really belongs here..
Debug trait
#[derive(Debug)] struct Point{x: i8, y: i8} let p = Point{x: 1, y: 2}; println!("{:?}", p); // print all fields on struct println!("{:#?}", p); // pretty-print struct dbg!(p) // prints the file, lineno, expression, and resultAccess Control
# users.rs pub struct User { // `struct` is public, so type can be returned outside of module pub id u8, // `id` is public, outside of module can access name String, // `name` is private, outside of module cannot assign or access }enums
TODO:
- are enums ordered? can you compare using < ?
- how to retrieve tuple/struct from enum outside of a match ?
Enum Basics
Enums in rust are used to enumerate all possible values,
but unlike many other languages, they can be parametrized and bind an arbitrary value.enum TaskStatus { Blocked, Ready, Started, Finished, } TaskStatus::ReadyYou can add enum-values to current scope (without namespace) with
use
keyword.use TaskStatus::*; let foo = Blocked; use TaskStatus::{Blocked, Ready}; let foo = Blocked; let bar = Started; // raises error, since not in scopeEnum Params
You can also store complex information in an enum,
encoding additional information with each possible enum value.enum Event { KeyPress(char), // wrapped type is tuple Click { x: i32, y: i32 }, // wrapped type is struct Blue = 0x0000ff, // wrapped type is primitive } Event::KeyPress('j') Event::Click{x: 100, y: 900}You can use the
match
keyword to extract information from these parametrized enum values.#[derive(Debug)] enum Pets { Cat(String, u8), Dog(String, u8, String), } let pet = Pets::Cat("maize", 1) let result = match pet { Pets::Cat(name, age) => { format!("A cat w/ name={name} age={age}", name=name, age=age) }, Pets::Dog(name, age, breed) => { format!("A {breed} dog, name={name}, age={age}", name=name, age=age, breed=breed) } } // "A cat w/ name=maize age=1"Methods
Like with structs, you can bind rust methods to enums.
enum Status { Ready, Started, Skipped, Completed, } impl Status { fn finished(&self) -> bool { match *self { Status::Completed | Status::Skipped => true; _ => false; } } } let s = Status::Skipped; println!("{}", s.finished)hashmaps
- homogenous
- heap allocated
- un-ordered
- objects implementing
Copy
trait will be copied, otherwise ownership given to maphttps://doc.rust-lang.org/std/collections/struct.HashMap.html
use std::collections::HashMap; let mut users = HashMap::new(); users.insert(0, "will"); // add/override entry to hash users.entry(1).or_insert("alex"); // add entry to hash if not exist let v: Option<&i32> = users.get(&0) // get entry from hash for (key, val) in &users { ... } // iterate over entries
Pointers
let foo = String::new("hi"); &foo // `&` get reference to foo let ref = &foo; *ref // `*` de-reference to get foo instancePointers
Function Pointers
References
Other
Option
The Option type (enum) stands in for
null
in rust.
It is commonly used withmatch
to extract the values.
It's methods are added to the prelude scope, so they are accessible without the namespace.https://doc.rust-lang.org/std/option/enum.Option.html
let val = Some("foo"); // `Option` instance, with value present let val = None; // `Option` instance, standin for null match val { Some(x) => println!("value was assigned"), // `x` here refers to whatever value is held by `Some` None => println!("value was not assigned"), }Result
Results are chainable enums with success/failure values.
For more details see rust errors.