A modern, multi-paradigm programming language.
# follow prompt to install rust toolchain using rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# check installation
rustc --version
# should print soemthing like "rustc 1.74.0 (79e9716c9 2023-11-13)""fn main() {
println!("Hello, Rust!");
}Create a file `hello.rs`
Installation
Compile and run
rustc hello.rs
./hello
# Hello, Rust!cargo new helloCreating a new package
cargo run # compile and run
cargo build # build
cargo test # run test
cargo add rand # add a cratefn main() {
let first_name = "Salama"; // &str
let last_name: = "Ashoush"; // &str
println!("Hello, {} {}!", first_name, last_name);
}Inference
fn main() {
let first_name: &str = "Salama";
let last_name: &str = "Ashoush";
println!("Hello, {} {}!", first_name, last_name);
}Type annotation
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");
}Shadowing
fn main() {
let mut first_name: &str = "Salama";
let last_name: &str = "Ashoush";
println!("Hello, {} {}!", first_name, last_name);
first_name = "Ramadan";
println!("Hello once more, {}!", first_name);
}Mutable variables
fn main() {
// constants should shout and init with type annotation
const ARE_YOU_COOL: bool = true;
println!("Are you cool?, {}!", ARE_YOU_COOL);
}Constants
numbers
floats
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}bool
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}Numeric operators
fn main() {
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
let truncated = -5 / 3; // Results in -1
// remainder
let remainder = 43 % 5;
}char
fn main() {
let c = 'z';
let z: char = 'ℤ'; // with explicit type annotation
let heart_eyed_cat = '😻';
}&str
fn main() {
let first_name = "Salama"; // &str (string slice)
// you can do lots of stuff with it, eg
first_name.len(); // 6
first_name.is_empty(); // false
first_name.contains("S"); // true
first_name.replace("S", "A"); // "Alama"
first_name.to_uppercase(); // "SALAMA"
first_name.to_lowercase(); // "salama"
first_name.chars(); // ['S', 'a', 'l', 'a', 'm', 'a'] (iterator)
first_name.split(" "); // ["Salama"]
}Tuple
fn main() {
let tup = (500, 6.4, 1);
// we can get the values by destructing
let (x, y, z) = tup;
println!("The value of y is: {y}");
// or by index
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
// Tuples can include mixed types
let person = ("Salama", 30, true);
}
Arrays
fn main() {
// array cant include mixed types
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
// using destructing
// you must match all the array
let [first, second, _3, _4, _5] = a;
// or use the .. to ignore the rest
let [first,second, ..] = a;
// using type annotation
let a: [i32; 5] = [1, 2, 3, 4, 5];
// init 3 element array with the same value
let a = [3; 5];
a.len(); // 3
// and many more
}
fn say_hello() {
println!("Hello, world!");
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn early_returns() {
if condition {
return something;
}
println!("otherwise!");
}
fn main() {
let a = 1;
let b = 2;
let c = add(a, b);
println!("{} + {} = {}", a, b, c);
say_hello();
early_returns();
}fn main() {
let number = 3;
// if expression
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
// this will error -> if coditions must evaluate to bool
if number {
println!("number was three");
}
// if else if
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
// after all if is an expression
let number = if condition { 5 } else { 6 };
// this would error?
let number = if condition { 5 } else { "six" };
}fn main() {
// infinite loop
loop {
println!("again!");
}
// loop with break
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break the loop and return the value
}
};
println!("The result is {}", result);
// for loop
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is {}", element);
}
// for loop with range
for number in 1..4 {
println!("{}!", number);
}
// while loop
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
}
fn main() {
// infinite loop
loop {
println!("again!");
}
// loop with break
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break the loop and return the value
}
};
println!("The result is {}", result);
// for loop
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is {}", element);
}
// for loop with range
for number in 1..4 {
println!("{}!", number);
}
// while loop
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
}
fn main() {
// vectors can only store values of the same type and are allocated on the heap
let mut v: Vec<i32> = Vec::new();
v.push(1);
// vector macro
let mut v2 = vec![1, 2, 3];
// length and capacity
v2.len(); // 3
v2.capacity(); // 3 (default)
v2.push(4);
v2.len(); // 4
v2.capacity(); // 6 (doubled)
// reading elements
let third: i32 = v[2];
println!("The third element is {}", third);
// reading elements with get
let third = v.get(2); // Some(&3)
// reading elements out of bounds
let does_not_exist = &v[100]; // panic
let does_not_exist = v.get(100); // None
// iterating over values
for i in v {
println!("{}", i);
}
// iterating over mutable references
for i in &mut v {
*i += 50;
}
}
Stack
Box.Heap
fn main() {
// ownership rules
// 1. Each value in Rust has a variable that’s called its owner.
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() appends a literal to a String
// 2. There can only be one owner at a time.
// moving ownership to another variable
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1); // error
// cloning the heap data (deep copy)
let s1 = String::from("hello");
let s2 = s1.clone();
// copy types
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
// 3. When a variable goes out of scope its value will be dropped
let s = String::from("hello");
takes_ownership(s);
println!("{}", s); // error
}
fn takes_ownership(some_string: String) {
// some_string comes into scope here
println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing memory is freed.
fn main() {
// 1. You can have as many immutable references as you want at any given time.
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// 2. You can have exactly one mutable reference.
let r3 = &mut s; // error
println!("{}", r3);
let r4 = &mut s; // error
// 3. You can't have a mutable reference while you have an immutable one.
let r5 = &s; // error you can't borrow `s` as mutable because it is also borrowed as immutable
println!("{}", r5);
r4.push_str(" world");
// Rust is smart enough to know that this is not a problem
let mut s2 = String::from("hello");
let r1 = &s2;
println!("{} and {}", r1, r2);
// r1 is no longer used after this point
let r2 = &mut s2;
println!("{}", r2);
r2.push_str(" world");
// r2 is no longer used after this point
let r3 = &s2;
println!("{}", r3);
}
struct User {
username: String,
email: String,
}
// tuple struct
struct Point(i32, i32, i32);
// unit struct
struct Unit
fn main() {
// create instance
let mut user = User {
username: String::from("user"),
email: String::from("ss@ss.com"),
};
println!("username: {}", user.username);
println!("email: {}", user.get_username());
// update
user.username = String::from("user_new");
println!("username: {}", user.username);
// update from other instance
let user2 = User {
username: String::from("user2"),
..user
};
let p = Point(1, 2, 3);
println!("p.0: {}", p.0);
let unit_struct = UnitStruct;
}// implementation block
impl User {
// constructor function (associated function) (no self)
fn new(username: String, email: String) -> Self {
Self {
username,
email,
sign_in_count: 1,
active: true,
}
}
// method (self is a reference to the instance of the struct)
fn get_username(&self) -> &String {
&self.username
}
// method (self is a mutable reference to the instance of the struct)
fn set_username(&mut self, username: String) {
self.username = username;
}
}
fn main() {
// calling associated function with ::
let mut user3 = User::new(String::from("user3"), String::from("ss@ss.com"));
// calling method with .
println!("username: {}", user3.get_username());
// calling method with . with mutable reference and passing String
user3.set_username(String::from("user3_new"));
println!("username: {}", user3.get_username());
}
// implementation block
impl User {
// constructor function (associated function) (no self)
fn new(username: String, email: String) -> Self {
Self {
username,
email,
sign_in_count: 1,
active: true,
}
}
// method (self is a reference to the instance of the struct)
fn get_username(&self) -> &String {
&self.username
}
// method (self is a mutable reference to the instance of the struct)
fn set_username(&mut self, username: String) {
self.username = username;
}
}
fn main() {
// calling associated function with ::
let mut user3 = User::new(String::from("user3"), String::from("ss@ss.com"));
// calling method with .
println!("username: {}", user3.get_username());
// calling method with . with mutable reference and passing String
user3.set_username(String::from("user3_new"));
println!("username: {}", user3.get_username());
}
// enums are types which have a few definite values
enum Movement {
Up,
Down,
}
// with values
enum Movement2 {
Up(u8),
Down(u8),
}
// impl block
impl Movement2 {
fn match_movement(&self) {
match self {
Movement2::Up(1) => println!("Up 1"),
Movement2::Down(1) => println!("Down 1"),
_ => println!("No match"),
}
}
}
fn main() {
// without values
let move_up = Movement::Up;
match move_up {
Movement::Up => println!("Moving up"),
Movement::Down => println!("Moving down"),
}
// with values
let move_down = Movement2::Down(1);
move_down.match_movement();
}struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
trait Pet {
fn talk(&self) -> String;
fn greet(&self) {
println!("Oh you're a cutie! What's your name? {}", self.talk());
}
}
impl Pet for Dog {
fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) }
}
impl Pet for Cat {
fn talk(&self) -> String { String::from("Miau!") }
}
fn main() {
let captain_floof = Cat { lives: 9 };
let fido = Dog { name: String::from("Fido"), age: 5 };
captain_floof.greet();
fido.greet();
}#[derive(Debug, Clone, Default)]
struct Player {
name: String,
strength: u8,
hit_points: u8,
}
fn main() {
let p1 = Player::default(); // Default trait adds `default` constructor.
let mut p2 = p1.clone(); // Clone trait adds `clone` method.
p2.name = String::from("EldurScrollz");
// Debug trait adds support for printing with `{:?}`.
println!("{:?} vs. {:?}", p1, p2);
}fn main() {
let name = "Löwe 老虎 Léopard Gepardi";
let mut position: Option<usize> = name.find('é');
println!("find returned {position:?}");
assert_eq!(position.unwrap(), 14); // will get the wrapped value or panic
position = name.find('Z');
println!("find returned {:?}", position);
assert_eq!(position.expect("Character not found"), 0); // expect is the same but with custom error
// you can do pattern matching on it
let l = match name.find('l') {
Some(position) => position,
None => 0,
};
}use std::fs::File;
use std::io::Read;
fn main() {
let file: Result<File, std::io::Error> = File::open("diary.txt");
match file {
Ok(mut file) => {
let mut contents = String::new();
if let Ok(bytes) = file.read_to_string(&mut contents) {
println!("Dear diary: {contents} ({bytes} bytes)");
} else {
println!("Could not read file content");
}
},
Err(err) => {
println!("The diary could not be opened: {err}");
}
}
}use std::collections::HashMap;
fn main() {
let mut page_counts = HashMap::new();
page_counts.insert("Adventures of Huckleberry Finn".to_string(), 207);
page_counts.insert("Grimms' Fairy Tales".to_string(), 751);
page_counts.insert("Pride and Prejudice".to_string(), 303);
if !page_counts.contains_key("Les Misérables") {
println!("We know about {} books, but not Les Misérables.",
page_counts.len());
}
for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
match page_counts.get(book) {
Some(count) => println!("{book}: {count} pages"),
None => println!("{book} is unknown.")
}
}
// Use the .entry() method to insert a value if nothing is found.
for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
let page_count: &mut i32 = page_counts.entry(book.to_string()).or_insert(0);
*page_count += 1;
}
println!("{page_counts:#?}");
// there are no hashmap! like vec! but you can create one from any iterator
let page_counts = HashMap::from([
("Harry Potter and the Sorcerer's Stone".to_string(), 336),
("The Hunger Games".to_string(), 374),
]);
}/// Pick `even` or `odd` depending on the value of `n`.
fn pick<T>(n: i32, even: T, odd: T) -> T {
if n % 2 == 0 {
even
} else {
odd
}
}
fn main() {
println!("picked a number: {:?}", pick(97, 222, 333));
println!("picked a tuple: {:?}", pick(28, ("dog", 1), ("cat", 2)));
}#[derive(Debug)]
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn coords(&self) -> (&T, &T) {
(&self.x, &self.y)
}
// fn set_x(&mut self, x: T)
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
println!("{integer:?} and {float:?}");
println!("coords: {:?}", integer.coords());
}fn duplicate<T: Clone>(a: T) -> (T, T) {
(a.clone(), a.clone())
}
// with where
fn duplicate<T>(a: T) -> (T, T)
where
T: Clone,
{
(a.clone(), a.clone())
}
fn main() {
let foo = String::from("foo");
let pair = duplicate(foo);
println!("{pair:?}");
}
Trait bounds
// Syntactic sugar for:
// fn add_42_millions<T: Into<i32>>(x: T) -> i32 {
fn add_42_millions(x: impl Into<i32>) -> i32 {
x.into() + 42_000_000
}
fn pair_of(x: u32) -> impl std::fmt::Debug {
(x + 1, x - 1)
}
fn pair_of<T: Debug>(x: u32) -> T {
(x + 1, x - 1)
}
fn main() {
let many = add_42_millions(42_i8);
println!("{many}");
let many_more = add_42_millions(10_000_000);
println!("{many_more}");
let debuggable = pair_of(27);
println!("debuggable: {debuggable:?}");
}Trait bounds using impl
fn main() {
let input = 'x';
match input {
'q' => println!("Quitting"),
'a' | 's' | 'w' | 'd' => println!("Moving around"),
'0'..='9' => println!("Number input"),
key if key.is_lowercase() => println!("Lowercase: {key}"),
_ => println!("Something else"),
}
}
fn main() {
describe_point((1, 0));
}
fn describe_point(point: (i32, i32)) {
match point {
(0, _) => println!("on Y axis"),
(_, 0) => println!("on X axis"),
(x, _) if x < 0 => println!("left of Y axis"),
(_, y) if y < 0 => println!("below X axis"),
_ => println!("first quadrant"),
}
}#[rustfmt::skip]
fn main() {
let triple = [0, -2, 3];
println!("Tell me about {triple:?}");
match triple {
[0, y, z] => println!("First is 0, y = {y}, and z = {z}"),
[1, ..] => println!("First is 1 and the rest were ignored"),
[.., b] => // tails ignore head,
[a@..,b] => // tail aliasing the head,
_ => println!("All elements were ignored"),
}
}fn sleep_for(secs: f32) {
let dur = if let Ok(dur) = std::time::Duration::try_from_secs_f32(secs) {
dur
} else {
std::time::Duration::from_millis(500)
};
std::thread::sleep(dur);
println!("slept for {:?}", dur);
}
fn main() {
sleep_for(-10.0);
sleep_for(0.8);
}fn sleep_for(secs: f32) {
let wahatever = if let Ok(d) = std::time::Duration::try_from_secs_f32(secs) {
d
} else {
std::time::Duration::from_millis(500)
};
std::thread::sleep(whatever);
println!("slept for {:?}", whatever);
}
fn main() {
sleep_for(-10.0);
sleep_for(0.8);
}fn sleep_for(secs: f32) {
let wahatever = if let Ok(d) = std::time::Duration::try_from_secs_f32(secs) {
d
} else {
std::time::Duration::from_millis(500)
};
std::thread::sleep(whatever);
println!("slept for {:?}", whatever);
}
fn main() {
sleep_for(-10.0);
sleep_for(0.8);
}fn sleep_for(secs: f32) {
let dur = if let Ok(dur) = std::time::Duration::try_from_secs_f32(secs) {
dur
} else {
std::time::Duration::from_millis(500)
};
std::thread::sleep(dur);
println!("slept for {:?}", dur);
}
fn main() {
sleep_for(-10.0);
sleep_for(0.8);
}mod foo {
pub fn do_something() {
println!("In the foo module");
}
}
mod bar {
pub fn do_something() {
println!("In the bar module");
}
}
fn main() {
foo::do_something();
bar::do_something();
}mod foo {
pub fn do_something() {
println!("In the foo module");
}
}
mod bar {
pub fn do_something() {
println!("In the bar module");
}
}
fn main() {
foo::do_something();
bar::do_something();
}https://wiki.mozilla.org/Areweyet