Rust
Updated: 2022-02-12 20:35:27+11:00
Please refer to Don Bright’s original rust-lang-cheat-sheet which I based much of this content off, as I work through the Rust in Motion course by Carol Nichols and Jake Goulding, rustlings and Rust in Action.
Rust in a nutshell⌗
- Syntax similar to C
- Ownership of memory enforced at compile time
- Statically linked
- Functional-ish, generic-ish, not so objecty
- Control flow using patterns,
match
keyword - Packages: ‘cargo’ command, https://crates.io
- Testing: cargo test,
#[test]
- Concurrency: ownership, mutability, channels, mutex, crossbeam + Rayon packages
- Auto formatter:
rustfmt filename.rs
- Compiler engine: LLVM
- Raw pointers, low level:
unsafe{}
keyword
Basics⌗
Documentation⌗
rustup doc
for offline docs!- doc.rust-lang.org standard library, primitive types, standard macros
- docs.rs for package documentation
Toolchain⌗
$ rustup.sh # install rust, see rust-lang.org for details
$ cargo new myproj # start new executable project
$ cargo new --bin myproj # as above
$ cargo new --lib myproj # start new library project
$ cd myproj # cd into the new directory
$ ls -lR # list our skeleton of files
src/main.rs # main.rs, has main() entry point
Cargo.toml # Cargo.toml defines packaging
$ $EDITOR Cargo.toml # add dependencies and other details
[package]
name = "helloworld"
version = "0.1.0"
authors = ["Benjamin Simmonds <ben@bencode.net>"]
[dependencies]
serde = "1.0.80"
$ cargo build # downloads dependencies + builds main.rs
$ cargo build --release # release build
$ cargo run # runs program created from main.rs
$ cargo test # runs tests (in parallel by default)
$ cargo test -- --test-threads=1 # run tests one at a time
$ cargo test -- --nocapture # run tests, show output
$ cargo run --example fundemo -- --argtodemo # run example with argument
Mutability⌗
let x = false; // all variable bindings are immutable by default
x = true; // compile error: can't change an immutable binding
let mut p = false; // "mut" designates a binding as mutable
p = true; // ok, mutable binding can change;
Types and variables⌗
let x: bool = false; // let keyword
let k = false; // rustc can determine some types, this is idiomatic rust
let y: char = '上'; // all chars are 4 bytes
let 上 = 5; // error. identifiers must be ASCII characters
let a: i8 = -2; // 8 bit signed integers, also i16, i32, i64
let b: u8 = 200; // 8 bit unsigned integers, also u16, u32, u64
let n: f32 = 0.45; // 32 bit float (automatcally converted+rounded from decimal to binary)
let n = 42.01f64; c // 64 bit float literal of the number 42.01 (approximately)
let r: [u8;3] = [3,4,5]; // array of 3 int, cannot grow
let s = [0;500]; // array of 500 integers, each initialized to 0
let s = &r[0..2]; // slice of array, s==&[3,4]
let s = &r[0..2][0]; // index into slice, s==3
let mut u:Vec<u8> = Vec::new(); // create empty vector of unsigned 8 bit int, can grow
let mut v = vec![3,4,5]; // initialize mutable vector using vec! macro
let w = vec![1,12,13]; // vectors can be immutable too
u.push( 2 ); // append item to vector
u.pop(); // vectors can return+remove last input (like a stack)
v.contains(&3); // true if vector contains value
v.remove(1); // remove the nth item from a vector...
v.append(u); // append v with u (u becomes empty ([]), both mutable)
v.extend(w); // extend v with w (v owns w, w can be immutable)
v.resize(200,0); // make vector have 200 elements, set them to 0
let x = &w[1..]; // get a slice of a vector (a view into it's elements)
print("{:?}",x); // [12,13];
let vs = v.len(); // length of vector
let (p,d,q) = (4,5,6); // tuple() can assign multiple variables at once
print("{}",p); // you can use them alone after tuple assignment
let m = (4,5,"a"); // tuples can have multiple different types as elements
let (a,b) = m.0, m.2; // tuple indexing with .0, .1, .2, etc
let (a,b) = m.p, m.q; // error, cannot index into a tuple using a variable
let (x, y, z) = m; // tuple destructuring into individual variables
let s = String::from("上善若水"); // String
let s2 = "水善利萬物而不爭"; // string literal, type is &str
let s3 = format!("{}{}",s2,s3); // concatenate strings
for i in "말 한마디에 천냥 빚을 갚는다".split(" ") {print!("{}",i);} // split string
let s4 = s.get(0..2); // Substring using indexes
let i4 = s.find('水').unwrap_or(-1); // find index of character (not a byte offset)
let hellomsg = r###" // Multi-line with embedded quotes
"Hello" in Chinese is 你好 ('Ni Hao')
"Hello" in Hindi is नमस्ते ('Namaste')
"###;
usize, isize // this is the pointer size. used in loops, vector length, etc
const BILBOG: i32 = 10; // constant
static ORGOG: &str = "zormpf"; // static, global-ish variable
static FOOBY: i32 = 5; // unsafely mutable
static Z_ERRMSG : [&str;2] = ["need input","need more input"]; // static strings
type Valid = bool; // typedef ( make your own type names )
let mut v = vec![1u8,2u8,3u8]; // determine the type of expression expr by looking at rustc error
println!("{}",v.iter_mut()); // for example, if we want to know the type of v, build an error
12 | println!("{}",v.iter_mut()); // type of v.iter_mut() is std::slice::IterMut<'_, u8>`
| ^^^^^^^^^^^^ `std::slice::IterMut<'_, u8>` <- error line tells you the type
Unit tests, integration tests⌗
Test functions are marked with the #[test]
attribute.
Unit tests should be placed in the same file as the code being tested.
./src/lib.rs:
pub fn process(v:&mut Vec<u8>)->&Vec<u8>{ v.update(|x| f(x)) } // main function called by users
fn f(x:u8)->u8 { x*x } // small piece of our code, to test in unit testing
#[cfg(test)] // cfg -> section will only compiled during 'cargo test'
mod tests { // namespace helper
use super::*; // bring in our functions above
#[test] // next function will be a single test
fn test_f() { assert!(f(4)==16); } // test f() by itself (unit)
}
Integration tests, for overall crate, lives under ./tests/*.rs
./tests/file.rs: // will only be built dring 'cargo test'
extern crate mypackage; // include package we are testing
#test // treat next function as a test
fn bigtest() { // not a unit test. instead, test overall code
let mut d = vec![1,2,3]; // set up some typical data users would have
let expected_results = vec![1,4,9]; // some results we expect
assert!(process(d)==expected_results); // test what a user would typically call, process()
}
$ cargo test # test build, will include cfg(test) sections
-> test_f passed # cargo reports on passed tests
-> test_bigtest failed # cargo reports on failed tests
Documentation⌗
rust-doc and cargo doc allow automatic building of html documentation for code. precede documentation of your code with three slashmarks instead of the normal two slashmarks, like so:
/// blorg() returns the blorgification of input x
/// # Details
/// this code implements the krishnamurthi procedure
/// for blimfication of zorgonautic primes
/// # Arguments
/// * `x` - typically a square number
/// # Safety
/// Cannot panic unless x overflows u64
/// # Example
/// let n = blorg(36);
fn blorg(x:u64)->u64 {
x+x*x
}
Then run rust-doc or cargo doc and view the result.
$ cargo doc
$ firefox target/doc/cratename/index.html
Good examples of the results are on https://crates.io
Resources⌗
Jon Gjengset is my hero. Not only is Jon a genius, he’s an absolute machine doing 5+ hour live coding streams tackling difficult and low-level problems using Rust, such as building your own TCP/IP stack from the ground up. Checkout his site, YouTube channel and Tweets for upcoming live stream which are not to be missed.