Rust
Homepage / Notes / Computer Science / Programming Languages / Rust
Basics
- Compile:
rustc {filename}.rs
- Run:
./{filename}
Anatomy
fn main() {
}
main
function is special: always the first code that runs in every executable Rust program. Function parameters go inside the parentheses: ()
And code for the function goes inside curly brackets: {}
Format
rustfmt
can be used to automatically format our Rust code. Indent 4 spaces, no tabs.
Comments
fn main() {
// This is a comment
}
Hello, World!
fn main() {
println!("Hello, World!");
}
Hello, World!
println!()
is a Rust macro. !
means it's a macro and not a function. ;
at the end of the line indicates that the expression is over.
Operators
https://doc.rust-lang.org/book/appendix-02-operators.html
Cargo
- New project:
cargo run {project_name}
- Build:
cargo build
, build for release:cargo build --release
(with optimizations) - Compile & run:
cargo run
- Check if project can compile:
cargo check
(faster thancargo build
)
Resources
Cargo book https://doc.rust-lang.org/stable/cargo/
Language Features
Primitives
Scalar Types
- Signed integers, unsigned integers
- Floating point
- Character:
'a'
,'b'
… - Boolean:
true
orfalse
()
which is an empty tuple
Compound Types
- Arrays
- Tuples
Variables
Mutable Variables
fn main() {
let x = 5 + 5;
+= 5;
x println!("{}", x);
}
error: Could not compile `cargoSivb4X`.
cannot assign twice to immutable variable
fn main() {
let mut x = 5 + 5;
+= 5;
x println!("{}", x);
}
15
Destructuring Assignment
Starting from Rust 1.59:
fn main() {
let (a, b) = (1, 2);
println!("{a}");
println!("{b}");
}
1
2
Functions
snakecase is the convention for naming functions
fn main() {
println!("{}", "Hello");
;
another_function()}
fn another_function() {
println!("{}", "World!");
}
Hello
World!
Function Arguments
Arguments' types have to be specified:
fn greet(name: &str) {
println!("Hello, {name}");
}
"Damien"); greet(
Hello, Damien
Function Return
Function's return type have to be specified too:
fn sum(x: i32, y: i32) -> i32 {
+ y
x }
println!("{}", sum(4, 5));
9
Function's automatically return the value of the last expression, but can be returned early using the return
keyword:
fn sum(x: i32, y: i32) -> i32 {
return x + y;
}
println!("{}", sum(4, 5));
9
Strings
fn main() {
let s = "Damien";
println!("{}", s);
}
Damien
String to chars
fn main() {
let s = "Damien".chars();
println!("{:?}", s);
}
Chars(['D', 'a', 'm', 'i', 'e', 'n'])
Numbers
let x = 9;
println!("{x}");
9
Ranges
let range = 1..5;
for i in range {
println!("{i}");
}
1
2
3
4
Right-inclusive range:
let range = 1..=5;
for i in range {
println!("{i}");
}
1
2
3
4
5
Formatted Print
fn main() {
let a = 1 + 1;
println!("{}", a);
}
2
Starting from Rust 1.58:
fn main() {
let a = 1 + 1;
println!("{a}");
}
2
https://www.rustnote.com/blog/format_strings.html
Data Structures
Sequence Collections
Tuple
Tuples can contain multiple types. Tuples have a fixed length.
let tup: (i32, f64, u8) = (500, 6.4, 1);
Array
Every element of an array has to have the same type. Array in Rust have a fixed length.
let a = [1, 2, 3, 4, 5];
Arrays are useful when you want your data to be allocated on the stack rather than the heap.
To write an array's type, you have to specify the type AND the number of elements in the array, separated by a colon, and enclosed in square brackets:
let a: [i32; 5] = [1, 2, 3, 4, 5];
It's possible to initialize an array that contains the same value for each element by specifying the initial value, followed by a semicolon, and then the length of the array, enclosed in square brackets:
let a = [3; 5];
println!("{:?}", a);
[3, 3, 3, 3, 3]
Accessing array elements
By using the index:
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
println!("{:?}", first);
println!("{:?}", second);
1
2
Vec
A type has to be specified when creating an empty vec
as type can't be inferred:
let v: Vec<i32> = Vec::new();
println!("{:?}", v);
[]
Not necessary when initializing the vec
with values:
let v = vec![1, 2, 3];
println!("{:?}", v);
[1, 2, 3]
Accessing values
let mut v = vec![1, 2, 3];
println!("{}", &v[0]);
println!("{}", &v[2]);
1
3
Adding values
When adding values later in the code, Rust can also infer the vec
type
let mut v = Vec::new();
.push(5);
v.push(6);
v.push(7);
v
println!("{:?}", v);
[5, 6, 7]
Removing values
let mut v = vec![1, 2, 3];
.remove(0);
v
println!("{:?}", v);
[2, 3]
Iterating over values
let v = vec![9, 10, 11];
for i in &v {
println!("{}", i);
}
9
10
11
Values can be mutated while iterating, but they have to be de-referenced by using *
let mut v = vec![9, 10, 11];
for i in &mut v {
*i += 1;
}
println!("{:?}", v);
[10, 11, 12]
VecDeque
A double-ended queue implemented with a growable ring buffer.
use std::collections::VecDeque;
let deq: VecDeque<u32> = VecDeque::new();
println!("{:?}", deq);
[]
Initializing with values:
use std::collections::VecDeque;
let deq = VecDeque::from([-1, 0, 1]);
println!("{:?}", deq);
[-1, 0, 1]
Push / Pop
use std::collections::VecDeque;
let mut deq: VecDeque<u32> = VecDeque::new();
.push_front(1);
deq.push_front(2);
deqprintln!("{:?}", deq);
[2, 1]
use std::collections::VecDeque;
let mut deq: VecDeque<u32> = VecDeque::new();
.push_back(1);
deq.push_back(2);
deqprintln!("{:?}", deq);
[1, 2]
use std::collections::VecDeque;
let mut deq = VecDeque::from([0]);
.push_front(1);
deq.push_front(2);
deq.push_back(3);
deq.push_back(4);
deq.push_back(5);
deqprintln!("{:?}", deq);
[2, 1, 0, 3, 4, 5]
use std::collections::VecDeque;
let mut deq = VecDeque::from([1, 2, 3]);
.pop_front();
deqprintln!("{:?}", deq);
[2, 3]
use std::collections::VecDeque;
let mut deq = VecDeque::from([1, 2, 3]);
.pop_back();
deqprintln!("{:?}", deq);
[1, 2]
LinkedList
A doubly-linked list with owned nodes.
Map Collections
HashMap
Allows to store key/value pairs.
use std::collections::HashMap;
let mut map = HashMap::new();
println!("{:?}", map);
.insert("Japan", "Tokyo");
map.insert("France", "Paris");
map.insert("Canada", "Ottawa");
mapprintln!("{:?}", map);
.remove("France");
mapprintln!("{:?}", map);
{}
{"France": "Paris", "Japan": "Tokyo", "Canada": "Ottawa"}
{"Japan": "Tokyo", "Canada": "Ottawa"}
BTreeMap
Equivalent to HashMaps
but "sorted".
use std::collections::BTreeMap;
let mut btree = BTreeMap::new();
println!("{:?}", btree);
.insert("Germany", "Berlin");
btree.insert("United Kingdom", "London");
btree.insert("Taiwan", "Taipei");
btreeprintln!("{:?}", btree);
.remove("United Kingdom");
btreeprintln!("{:?}", btree);
{}
{"Germany": "Berlin", "Taiwan": "Taipei", "United Kingdom": "London"}
{"Germany": "Berlin", "Taiwan": "Taipei"}
Set Collections
HashSet
Set form of HashMap
, meaning no duplicate keys are allowed.
use std::collections::HashSet;
let mut set = HashSet::new();
.insert("key");
set.insert("key");
setprintln!("{:?}", set);
{"key"}
Note how "key" is only present once, not twice.
BTreeSet
Set form of BTreeMap
.
use std::collections::BTreeSet;
let set: BTreeSet<u32> = BTreeSet::new();
Structs
A struct contains fields. Access value by dot notation.
#[derive(Debug)]
struct User {
: bool,
active: String,
username: String,
email: u64,
sign_in_count}
let mut user1 = User {
: String::from("[email protected]"),
email: String::from("someusername123"),
username: true,
active: 1,
sign_in_count};
println!("{:?}", user1);
println!("{:?}", user1.email);
.email = String::from("[email protected]");
user1println!("{:?}", user1.email);
User { active: true, username: "someusername123", email: "[email protected]", sign_in_count: 1 }
"[email protected]"
"[email protected]"
Field Init Shorthand
#[derive(Debug)]
struct User {
: bool,
active: String,
username: String,
email: u64,
sign_in_count}
let email = String::from("[email protected]");
let username = String::from("[email protected]");
let mut user1 = User {
, // instead of email: email,
email, // instead of: username: username,
username: true,
active: 1,
sign_in_count};
println!("{:?}", user1);
User { active: true, username: "[email protected]", email: "[email protected]", sign_in_count: 1 }
Struct Update Syntax
#[derive(Debug)]
struct User {
: bool,
active: String,
username: String,
email: u64,
sign_in_count}
let user1 = User {
: String::from("[email protected]"),
email: String::from("someusername123"),
username: true,
active: 1,
sign_in_count};
let user2 = User {
: String::from("[email protected]"),
email..user1
};
println!("{:?}", user2);
User { active: true, username: "someusername123", email: "[email protected]", sign_in_count: 1 }
Defining Methods
struct Rectangle {
: u32,
width: u32,
height}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
let rect1 = Rectangle {
: 30,
width: 50,
height};
println!("{:}", rect1.area());
1500
Control Flow
If/else statements
fn sum(x: i32, y: i32) -> i32 {
return x + y;
}
if sum(4, 5) > 10 {
println!("This is impossible");
} else if sum(4, 5) == 9 {
println!("This is correct");
} else {
println!("This is impossible");
}
This is correct
Inline conditional
let x = if true { 1 } else { 0 };
println!("{}", x);
1
Loop
let mut counter = 0;
loop {
if counter == 5 {
break;
}
+= 1;
counter println!("(:");
}
(:
(:
(:
(:
(:
Loops can return a value by providing it to the break
keyword:
let mut counter = 0;
let nth = loop {
if counter == 5 {
break counter;
}
+= 1;
counter };
println!("Loop has been executed {nth} times");
Loop has been executed 5 times
Labeled loops
By default, break
applies to the innermost loop. You can specify a loop label to break a specific loop.
let mut count_one = 0;
let mut count_two = 10;
'loop_one: loop {
println!("count_one = {count_one}");
loop {
println!("count_two = {count_two}");
if count_two == 0 {
break;
}
if count_two == 5 {
break 'loop_one;
}
-= 1;
count_two }
+= 1;
count_one }
count_one = 0
count_two = 10
count_two = 9
count_two = 8
count_two = 7
count_two = 6
count_two = 5
While
let mut number = 0;
while number != 6 {
println!("{number}");
+= 1;
number }
0
1
2
3
4
5
Iterators
Since Rust 1.23, no need to call .iter()
fn main() {
for i in [1, 2, 3] {
println!("{}", i);
}
}
1
2
3
Iterator methods
https://doc.rust-lang.org/std/iter/trait.Iterator.html
Reverse
for i in (0..4).rev() {
println!("{i}");
}
3
2
1
0
Min/Max
println!("{}", [1, 2, 3].iter().min().unwrap());
1
println!("{}", [1, 2, 3].iter().max().unwrap());
3
Last
println!("{}", [1, 3, 5].last().unwrap());
5
Map
println!("{:?}", [1, 3, 5].map(|x| 2 * x));
[2, 6, 10]
Filter
1..20).filter(|x| x % 3 == 0).for_each(|i| println!("{}", i)); (
3
6
9
12
15
18
Fold / Reduce
let a = [1, 2, 3];
let sum = a.iter().fold(0, |acc, x| acc + x);
println!("{}", sum);
6
Misc
Naming conventions: https://doc.rust-lang.org/1.0.0/style/style/naming/README.html
No garbage collection: have to manage memory yourself
Pattern matching through match
:)
Packages
Yew
https://yew.rs/docs/intro/ http://www.sheshbabu.com/posts/rust-wasm-yew-single-page-application/
Yew is a modern Rust framework for creating multi-threaded front-end web apps using WebAssembly.
Sauron
https://github.com/ivanceras/sauron
Sauron is a versatile web framework and library for building client-side and/or server-side web applications with strong focus on simplicity. It is suited for developing web application which uses progressive rendering.
Iced
A cross-platform GUI library for Rust, inspired by Elm
Poem
https://github.com/poem-web/poem
A full-featured and easy-to-use web framework with the Rust programming language.
create-rust-app
https://github.com/Wulf/create-rust-app
Set up a modern rust+react web app by running one command.
Leptos
https://github.com/leptos-rs/leptos
Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.
Sycamore
https://sycamore-rs.netlify.app/
A reactive library for creating web apps in Rust and WebAssembly
Resources
Main
- https://doc.rust-lang.org/book/
- https://doc.rust-lang.org/rust-by-example/
- https://learnxinyminutes.com/docs/rust/
- https://cheats.rs/ Rust Language Cheat Sheet
- https://github.com/rust-lang/rustlings/ Small exercises to get you used to reading and writing Rust code!
- https://google.github.io/comprehensive-rust/ Rust for Android
- https://www.lurklurk.org/effective-rust/
Other
- Zola, a static site generator https://www.getzola.org/
- Strings: https://www.brandons.me/blog/why-rust-strings-seem-hard
- https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/
- https://fasterthanli.me/articles/a-half-hour-to-learn-rust
- Rust notebook: https://blog.abor.dev/p/evcxr
- Web Fullstack Framework: https://github.com/MoonZoon/MoonZoon
- Frontend framework: https://github.com/seed-rs/seed
- https://blog.logrocket.com/what-you-cant-do-in-rust-and-what-to-do-instead/
- Written in Rust alternatives of other softwares: https://github.com/TaKO8Ki/awesome-alternatives-in-rust
- Serde is a framework for serializing and deserializing Rust data structures efficiently and generically: https://serde.rs/
- A lunatic web framework for the Rust language: https://github.com/lunatic-solutions/submillisecond
- A Rust API search engine: https://roogle.hkmatsumoto.com/
- https://rauljordan.com/rust-concepts-i-wish-i-learned-earlier/
axohtml
(type-checked JSX for Rust) https://github.com/axodotdev/axohtml- Polars: Lightning-fast DataFrame library for Rust and Python https://www.pola.rs/