Some Notes while starting to learn Rust
Useful ressources
- Rustonomicon: https://doc.rust-lang.org/nomicon/index.html
- Rust-book: https://doc.rust-lang.org/book/
- Rust-by-example: https://doc.rust-lang.org/rust-by-example/
Setup
Automatic Formatting with rustfmt
cargo fmt
to format the whole project according to community code style guidelines.cargo fix
to fix some compiler warnings automatically, if they have a clear way (stay away from while learning)- optional linter:
rustup component add clippy
cargo clippy
Cargo
- create a new project:
cargo new hello_project
- create a new project inside existing folder:
cargo new
cargo build
builds a binary to./target/debug/projectname
cargo run
to build and run.cargo check
to check for compiler errors (nice if building takes longer)cargo build --release
to compile it with optimizations
Basics
Documentation
// comment
/* block comment */
/// Generate library docs for the following item. support MARKDOWN!
//! Generate library docs for the enclosing item.
cargo doc --open
to compile all included md-documentation to a searchable html.- The style of doc comment //! adds documentation to the item that contains the comments rather than to the items following the comments. We typically use these doc comments inside the crate root file (src/lib.rs by convention) or inside a module to document the crate or the module as a whole:
src/lib.rs
//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.
/// Adds one to the number given.
Print out
handled by a series of macros defined in std::fmt
format!
formatted text to Stringprint!
println!
pinted to console (io::stdout)eprint!
eprintln!
printed to the standard error (io::stderr)
// in general the {} will be automatically replaced with the stringified args.
println!("{subject} {verb} {object}",
object="He",
subject=10,
verb="aged by");
println("{0}, this is {1}. {0} how are you?", "Alice", "Bob");
// Different formatting can be invoked by specifying the format character :
println!("Base 10: {}", 69420); // 69420
println!("Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("Base 8 (octal): {:o}", 69420); // 207454
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c
println!("Base 16 (hexadecimal): {:X}", 69420); // 10F2C
// Min length right-justify text: (adding whitespaces to the left:)
println!("{number:>5}", number=1); // -> 1
// pad numbers with extra zeros
println("{number:0<5}", number=1); // -> 10000
// named arguments in the format specifier by appending a $
println("{numb:0>width$}", numb=1, width=5); // -> 00001
// capture variales:
let number: f64 = 1.0;
let width: usize = 5;
println!("{number:>width$}"); // -> 1
- fmt::Debug: Uses the {:?} marker. Format text for debugging purposes.
- that way we can "print" structs etc, that do not implement
std::fmt::Display
, the default toString (interface?)
println!("{1:?} {0:?} is the {actor:?} name.",
"Slater",
"Christian",
actor="actor's");
- pretty printing by using
{:#?}
extends thefmt::Debug
#[derive(Debug)]
struct Person<'a> {
name: &'a str,
age: u8
}
fn main() {
let name = "Peter";
let age = 27;
let peter = Person { name, age };
// Pretty print
println!("{:#?}", peter);
}
Importing
use std::io;
io::stdin()
// would be the same as:
std::io::stdin()
Exporting
This is useful if we were to build a library. Instead of long paths import paths that make sense logically for our programm like mylib::api::util::types::some_struct
we can re-export to make it easy accessable: mylib::some_struct
- in the example the points of interest could be PrimaryColor, SecondaryColor and the mix function. So we move them to the public API.
- this will also move the autogenerated documentation for the public API elements (from
/// # h1
markdown) to the frontpage
//! # Art
//!
//! A library for modeling artistic concepts.
// So we re-export them to the public API:
pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;
//
pub mod kinds {
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
// --snip--
}
}
Common Programming Concepts
Variables and Mutability
let mut
to indicate the value can be reassigned. (so even changed from a int -> string)const
must be without mut. Also types (ex. Uint/String) have to be assigned.
Number literals:
- you can use
_
as a visual separator to make numbers more human readable.
Decimal 98_222
Hex 0xff
Octal 0o77
Binary 0b1111_0000
Byte (u8 only) b'A'
Integer Overflow
- when in debug mode, rust checks for overflows that will panic
- when building with
--releaste
there is no overflow checking. Just the as expected Overflowing.
Compond Types
Touple
- fixed length once declared. Can hold multiple different types.
let tup1: (i32, f64, u8) = (500, 6.4, 1);
let tup2 = (500, 6.4, 1);
let (x,y,z) = tup2; // destructuring is possible to get individual values
let mid = tup1.0; // refferencing by index is possible aswell
- the touble without any values has a special name: unit. This value and its type are written
()
and represent an empty value or empty return type. Expressions implicitly return this (if nothing else is returned).
Array
- fixed length and can hold only one type.
let a = [1,2,3];
let b: [i32; 5] = [1,2,1,2,3];
let same: [3;6]; // all 5 entries are the same value(3) -> [3,3,3,3,3,3]
Vector
- unlike the array a vector is not fixed in size.
Statements vs Expressions
- Statements are everything without a return value.
- Expressions evaluate to a value.
- Examples: calling a fn, calling a macro, a new scope block created with Curly-Braces.
- Expressions do not include ending semicolons.
// expression example
let y = {
let x = 3;
x + 10
};
println!("The value of y is: {y}"); //-> The value of y is: 13
- blocks of code always evaluate to the last expression in them.
- we can use expressions as implicit return in functions:
fn plus_one(x: i32) -> i32 {
x + 1;
}
using if in a let Statement
let condition = true;
let number = if condition {5} else {6};
Different Loops
loop
- we can use breaks optional value to pass a value out of the scope of the loop itself:
let mut counter =0;
let result = loop{
counter += 1;
println!("going again");
if counter == 5{
break counter * 10;
}
}
println("result is {result}");
- we can label loops to distinguish break and continue for inner and outer loops.
let mut count = 0;
'counting_up: loop{
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaing == 9 {
break;
}
if count == 2{
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}")
while loop
while number != 0 {
number -= 1;
}
for loop
let a = [10,20,30,40,50];
for element in a {
println!("the value is: {element}")
}
// using for loop with a Range (1..4). .rev() to reverse said Range:
for nr in (1..4).rev() {
println!("{nr}")
}
// 3
// 2
// 1