Pragmatic Rust
I. Rust install
Debian details:
Debian Linux has rustc, and using apt install works fine, but doesn't include cargo (rust package manager),
- but should use cargo
- So www.rustup.rs -install.
- adds ~\.cargo
- adds to ~\.profile
- adds rustup for itself, and cargo and rustc for rust-compiler
For other OS: use rustup.rs
In Vim, add Ale in vim plugins dir for in-editor syntax & compilation errors.
II. Rust project
$ cargo init
(edit src/main.rsi Hello -text, save)
$ cargo run
What is #[test]?
A: Test code for this file, see next answer:
What is #[cfg(test)]?
A: Instruction for cargo build tool that the next code is test module, and contains 'mod tests' -block. To run tests use
$ cargo test
Tests are also run when
$ cargo run
See more: https://doc.rust-lang.org/1.5.0/book/testing.html
III. Rust proggy
(todo)
IV. Debugging Rust
How to see asm output of the compiler?
https://github.com/gnzlbg/cargo-asm
- A cargo subcommand that displays the assembly or llvm-ir generated for Rust source code.
Rust compiler output to see parse tree and resulting intermediate code?
(todo)
Rust debugging with dfb?
1. Compile
2. set debugger to single thread mode
3. > gdb rust-executable
4. (gdb) run
5. How to set breakpoint, view stack or heap?
(gdb) break functionName
(gdb) run
(gdb) frame
(gdb) n
Rust Editions
- When we want to release a feature that would otherwise be backwards incompatible, we do so as part of a new Rust edition.
- Editions 2015, 2018, 2021 https://doc.rust-lang.org/edition-guide/editions/index.html
V. Rust Theory
- Olisko siistiä jos tää oliskin rust-ohjelma jota voi lukea
Rust concepts
- Bit pattern
0b001010110110
- Eventually all types converted into a bit pattern.
- Floating points f32
0.0 == (-0.0)
- stack, heapo
- Variables
- pass something by value
- move when passed in function, copy when int/char/primitive
- ownership/borrowing &
- "&" - pass something by reference - f.ex. "&my_var"
calculate_sum( &entries );
fn calculate_sum(index: &vec) -> Integer { index = do stuff ... }
- Aliasing Model https://plv.mpi-sws.org/rustbelt/stacked-borrows/
- mutating mut vars are passed/defined in fn with "&mut"
- &str[4..5] - string slice type -> "&str"
- points to a slice of the binary
- also type &[i32]
- Struct
struct Stuff {
num: f32;
}
Stuff { num: 1.1; }
- Trait
- https://doc.rust-lang.org/reference/types/trait-object.html
- collection of methods for an unknown type Self.
- Almost like abstract base class or an interface.
- How to see what functions to implement for a trait?
22:30 < palindrome> how do I know from the docs which trait my struct has to implement in
order to use BinaryHeap ?
22:42 < ChaiTRex> palindrome: Two places to look are the struct definition and the impl
blocks containing the methods or associated functions you want to use.
For example, let's say you want to use the new associated function. Go to
the new associated function in the docs and scroll up to the impl block
it's just under. You'll see it's impl BinaryHeap where T: Ord.
https://doc.rust-lang.org/src/alloc/collections/binary_heap.rs.html#350-809
- traits can't have async fns
- Example:
trait Movement {
fn forward(&mut self, speed: f32) {};
fn turn(&mut self, direction: f32) {};
}
struct Sentinel;
impl Movement for Sentinel {
fn forward(&mut self, speed: f32) {};
fn turn(&mut self, direction: f32) {};
}
- Example:
- cAMP: What is Vec>>
- Alexendoo: dyn Foo + Send is a trait object that implements Foo and is Send.
- Ever wanted to give your things some default value? Like, give an enumerable or structs default value? Default trait https://doc.rust-lang.org/std/default/trait.Default.html
- impl Default for SomeEnumerables { ... }
- https://doc.rust-lang.org/std/marker/trait.Send.html
- Lifetimes
- Errors
- Errors are returned.
- Result type
- No exceptions!
- inrecoverable get panic
panic("My panic message")
- Library should have custom error types, prefer not to panic
enum Result {
Ok(T),
Err(E),
}
fn init() -> Result<(), ie::Error> {
let mut file = match File::create("ferris.txt") {
Ok(f) => f,
Err(e) => return Err(e)
};
// this one can be written as below
match file.write_all("Foo") {
Ok(_) => Ok(()),
Err(e) => return Err(e)
}
// with a question mark that returns result or error
file.write_all("Foo")?
// with unwrap result or panics
file.write_all("Foo").unwrap()
// expect
// map_err
}
Rust REPL??
Rust inline-debugger?
Rust linting
VI. Rust idioms
- Vectors
12:11 < frogzilla> Easiest way to convert Vec, E>> to Vec?
12:13 < dpc1> map, unwrap(x2), collect
- Closures - closure expressions https://doc.rust-lang.org/reference/types/closure.html
- unique anonymous type that cannot be written out
- Error Handling question mark
- ? mark at the end of a statement "pops the error out, and exits the handler"
- Match
- The "@ operator" https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#-bindings
- Creates a variable while matching the value of the variable
- Rust 1.56 allows bindings in this @ operator pattern
- Slice
https://adventures.michaelfbryan.com/posts/daily/slice-patterns/?utm_source=reddit&utm_medium=social&utm_campaign=daily-rust-slice-patterns
- Clone trait
#[derive(Clone)]
dostuff(numbers.clone()):
- PartialEq
let bit_pattern = 32424;
f32::frombits(bit_pattern) == f32::from_bits(bit_pattern)
(f32::NAN).to_bits()
- Rc and Arc
- An example of a non-Send type is the reference-counting pointer rc::Rc. If two threads attempt to clone Rcs that point to the same reference-counted value, they might try to update the reference count at the same time, which is undefined behavior because Rc doesn’t use atomic operations. Its cousin sync::Arc does use atomic operations (incurring some overhead) and thus is Send.
- underline
- when name does not matter
- elide type
- receive must_use results
- asterisk
dereference?
*&T ?
- open file? stream?
- match
- self
- traits
- Deref trait
Allows type to turn into other types.
*std::ops::Deref::deref(&x) in an immutable place expression context
- Iterator trait
impl Iterator for WhatEver {
type tuff = Stuff;
fn next(&mut self) -> Option<> {
/* next -> Option */
}
}
- Display trait
Allows to print types to console.
How to implement Display?
use std::fmt;
impl fmt::Display for City2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/* this line is workaround for editor syntax highlight '> */
write!(f, "City<{}>", self.name);
}
}
VII. Rust Crates
- Axum web framework https://tokio.rs/blog/2021-07-announcing-axum
VIII. Rust on Linux
https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/coding-guidelines.rst
For Linux kernel related code, write as much of your code with safe.
Use unsafe blocks inside unsafe functions.
- like:
pub unsafe fn load(p: *const i32) { unsafe { *p } }
- Use unsafe fn denotation, with unsafe blocks inside.
- Document the safety requirements with /// # Safety -blocks:
// SAFETY: the returned string is foo bar etc cetera
Uphold type invariants
- idea is to constrain types to safe knowns, maintain an invariant, avoid going outside that definition.
- Document invariants with /// # Invariant -blocks:
// INVARIANT: foo is guaranteed to be xyz of strBar
Document examples for function, type, trait, module and crate
- with /// Examples -blocks:
Document modules
- using //!: First line of module description comment block
For examples see core::ptr::read
Write tests, as tests will be integrated with (CI) builds in the future.
Rust examples?
APPENDIX
Rust Webassembly Cloud Project - Krustlet
https://github.com/krustlet/krustlet
Random stuff
TESTING
18:40 < CuriousErnestBro> I'm running a teardown() functions at the end of every test. but the tests are full with .unwrap()'s and sometimes they fail, and I still want teardown() to run, how2rust?
18:47 < trev> CuriousErnestBro just encapulate the test part (put into another fn for cleanliness?) and have it return Result so you can unwrap, then put it between init() and teardown()
18:47 < trev> not unwrap, but ?
18:48 < trev> probably need anyhow::Result for that