Skip to content
Tomas Tulka edited this page Nov 28, 2024 · 20 revisions
fn main() {
    println!("Hello, world!");
}

Cargo

cargo --version
cargo new hello
cargo new hello --lib
cargo check
cargo run
cargo run -- arg1 arg2
cargo build
cargo build --release
cargo update
cargo test

Modules

mod front_of_house {
  pub mod hosting {
    pub fn add_to_waitlist() {}
  }
}
pub fn eat_at_restaurant() {
  // Absolute path
  crate::front_of_house::hosting::add_to_waitlist();
  // Relative path
  front_of_house::hosting::add_to_waitlist();
}
fn deliver_order() {}

mod back_of_house {
  fn fix_incorrect_order() {
    cook_order();
    super::deliver_order();
  }

  fn cook_order() {}
}
use crate::front_of_house::hosting as host;

pub fn eat_at_restaurant() {
  host::add_to_waitlist();
}

I/O

use std::io;
...
let mut input = String::new();
io::stdin()
  .read_line(&mut input)
  .expect("Failed to read line");

let parsed_num: u32 = match input.trim().parse() {
  Ok(num) => num,
  Err(_) => 0,
};
let x = 5;
let y = 10;
println!("x = {x} and y + 2 = {}", y + 2);

Command Line

// cargo run -- first second
let args: Vec<String> = env::args().collect();
let first = &args[1];
let second = &args[2];

Files

use std::fs;

let contents = fs::read_to_string(file_path)
  .expect("Cannot read the file");

Envvars

use std::env;

let myvar = env::var("MYVAR").unwrap_or_else("default")

Debugging

#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}
fn main() {
  let rect1 = Rectangle {
    width: 30,
    height: 50,
  };
  println!("rect1 is {rect1:?}"); // or {:#?}
}
fn main() {
  let scale = 2;
  let rect1 = Rectangle {
    width: dbg!(30 * scale),
    height: 50,
  };
  dbg!(&rect1);
}

Data Types

Integers

Length Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

Floating Point

Length Signed
32-bit f32
64-bit f64

Boolean

let t = true;
let f: bool = false;

Characters

let c = 'z';
let z: char = 'ℤ';
let heart_eyed_cat = '😻';

Tuples

let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
let five_hundred = tup.0;

Arrays

let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // [3, 3, 3, 3, 3]

let first = a[0];
let second = a[1];
struct User {
  active: bool,
  email: String,
}

let mut user1 = User {
  active: true,
  email: String::from("a@example.com"),
};
user1.email = String::from("b@example.com");
let email = user1.email;

let user2 = User {
  active: false,
  email
}
let user3 = User {
  active: false,
  ..user1
}
struct Point(i32, i32, i32);

let origin = Point(0, 0, 0);
struct AlwaysEqual;

let subject = AlwaysEqual;

Struct Methods

struct Rectangle {
  width: u32,
  height: u32,
}
impl Rectangle {
  fn area(&self) -> u32 {
    self.width * self.height
  }
}

let rect1 = Rectangle {
  width: 30,
  height: 50,
};
rect1.area()
impl Rectangle {
  fn square(size: u32) -> Self {
    Self {
      width: size,
      height: size,
    }
  }
}
type Kilometers = i32;
// The never type that never returns
fn bar() -> ! {
    ...
}

Variables

Mutable variables

let mut x = 5;
x = 6;

Constants

const PI_VALUE: f32 = 3.14;

Shadowing

let x = 5;
let x = x + 1;
{
  let x = x * 2;
   println!("x in the inner scope is: {x}"); // 12
}
println!("x is: {x}"); // 6

Ownership

fn main() {
  let s = String::from("hello");  // s comes into scope

  takes_ownership(s);             // s's value moves into the function...
                                  // ... and so is no longer valid here

  let x = 5;                      // x comes into scope

  makes_copy(x);                  // x would move into the function,
                                  // but i32 is Copy, so it's okay to still
                                  // use x afterward
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
  // special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
  println!("{some_string}");
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
  println!("{some_integer}");
} // Here, some_integer goes out of scope. Nothing special happens.
&i32        // a reference
&'a i32     // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  if x.len() > y.len() {
    x
  } else {
    y
  }
}
struct Excerpt<'a> {
    part: &'a str,
}
impl<'a> Excerpt<'a> {
  fn level(&self) -> i32 {
    3
  }
}
let s: &'static str = "I have a static lifetime.";

Functions

fn main() {
  let five = my_function(5);
}
fn my_function(x: i32) -> i32 {
  println!("The value of x is: {x}");
  x
}
fn main() {
  let s1 = String::from("hello");
  let (s2, len) = calculate_length(s1);
  println!("The length of '{s2}' is {len}.");
}
fn calculate_length(s: String) -> (String, usize) {
  let length = s.len(); // len() returns the length of a String
  (s, length)
}
fn main() {
  let s1 = String::from("hello");
  let len = calculate_length(&s1);
  println!("The length of '{s1}' is {len}.");
}
fn calculate_length(s: &String) -> usize {
  s.len()
}
fn main() {
  let mut s = String::from("hello");
  change(&mut s);
}
fn change(some_string: &mut String) {
  some_string.push_str(", world");
}
let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{r1} and {r2}");
// variables r1 and r2 will not be used after this point

let r3 = &mut s; // no problem
println!("{r3}");

Lambdas

fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;

Closures:

let list = vec![1, 2, 3];
let only_borrows = || println!("From closure: {list:?}");
only_borrows();
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
  Box::new(|x| x + 1)
}

Function pointers:

fn add_one(x: i32) -> i32 {
    x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}
let answer = do_twice(add_one, 5);

Conditionals

let number = 6;

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");
}
let condition = true;
let number = if condition { 5 } else { 6 };

Loops

loop {
  ...
  continue;
  ...
  break;
}
let mut counter = 0;

let result = loop {
  counter += 1;
  if counter == 10 {
    break counter * 2;
  }
};

Labels

'first_loop: loop {
  ...
  loop {
    if ... {
      break;
    }
    else {
      break 'first_loop;
    }
  }
}

While

while number > 0 {
  number -= 1;
}

For

let a = [10, 20, 30, 40, 50];

for element in a {
  println!("the value is: {element}");
}
for number in (1..4).rev() {
  println!("{number}!"); // 3! 2! 1!
}

Strings

let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {s1}, s2 = {s2}");
let data: &str = "hello"; // string slice
let s: String = data.to_string(); // string box
let mut s = String::from("he");
s.push_str("ll");
s.push('o');
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}");

Slices

let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];
let s = String::from("abcde");

let slice = &s[3..len];  // de
let slice = &s[3..];     // de
let slice = &s[0..len];  // abcde
let slice = &s[..];      // abcde
fn main() {
    let s1 = String::from("s1");
    let s2 = "s2";

    print_slice(&s1);
    print_slice(s2);
}

fn print_slice(s: &str) {
    println!("slice: {s}");
}

Enums

enum IpAddrKind {
  V4,
  V6,
}
let four = IpAddrKind::V4;
fn route(ip_kind: IpAddrKind) {}
enum IpAddr {
  V4(String),
  V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
enum IpAddr {
  V4(u8, u8, u8, u8),
  V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
struct Ipv4Addr { ... }
struct Ipv6Addr { ... }
enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}

Option Enum

Defined by the standard library:

enum Option<T> {
    None,
    Some(T),
}
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;

Pattern Matching

let dice_roll = 9;
match dice_roll {
  3 => add_fancy_hat(),
  7 => remove_fancy_hat(),
  _ => reroll(),  // or `_ => ()`
}
enum Coin { Penny, Nickel, Quarter, }

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => { println!("Lucky penny!"); 1 }
        Coin::Nickel => 5,
        Coin::Quarter => 25,
    }
}
fn plus_one(x: Option<i32>) -> Option<i32> {
  match x {
    None => None,
    Some(i) => Some(i + 1),
  }
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
let config_max = Some(3u8);
if let Some(max) = config_max {
  println!("The maximum is configured to be {max}");
}

Collections

Vectors

let v: Vec<i32> = Vec::new();
v.push(1);

let v = vec![1, 2, 3];
let third: &i32 = &v[2];
let third: Option<&i32> = v.get(2);

for i in &v {
  println!("{i}");
}

let mut v = vec![100, 32, 57];
for i in &mut v {
  *i += 50;
}
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
  println!("{top}");
}
let v = vec!['a', 'b', 'c'];

for (index, value) in v.iter().enumerate() {
  println!("{value} is at index {index}");
}

Hash Map

use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

let team_name = String::from("Blue");
let score = scores.get(&team_name).copied().unwrap_or(0);

scores.entry(String::from("Yellow")).or_insert(50);

for (key, value) in &scores {
  println!("{key}: {value}");
}

Errors

panic!("crash and burn");
enum Result<T, E> {
  Ok(T),
  Err(E),
}

let greeting_file = match File::open("hello.txt") {
  Ok(file) => file,
  Err(error) => panic!("Problem opening the file: {error:?}"),
};
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
  let mut username = String::new();
  File::open("hello.txt")?.read_to_string(&mut username)?;
  Ok(username)
}

Generics

struct Point<T> {
  x: T,
  y: T,
}
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
struct Point<X1, Y1> {
  x: X1,
  y: Y1,
}
impl<X1, Y1> Point<X1, Y1> {
  fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
    Point {
      x: self.x,
      y: other.y,
    }
  }
}
impl Point<f32> {
  fn distance_from_origin(&self) -> f32 {
    (self.x.powi(2) + self.y.powi(2)).sqrt()
  }
}
pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct Article {
    pub headline: String,
    pub content: String,
}
impl Summary for Article {
    fn summarize(&self) -> String {
        format!("News: {}", self.headline)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
}
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

let tweet = Tweet {
  username: String::from("horse_ebooks"),
  content: String::from("of course, as you probably already know, people"),
};
println!("1 new tweet: {}", tweet.summarize());

pub fn notify(item: &impl Summary) {
  println!("Breaking news! {}", item.summarize());
}
// is syntactic sugar for:
pub fn notify<T: Summary>(item: &T) {
  println!("Breaking news! {}", item.summarize());
}

pub fn notify(item: &(impl Summary + Display)) { ...
// is syntactic sugar for:
pub fn notify<T: Summary + Display>(item: &T) { ...
// default implementation (optional):
pub trait Summary {
  fn summarize(&self) -> String {
    String::from("(Read more...)")
  }
}

pub struct Article {
    pub headline: String,
    pub content: String,
}
impl Summary for Article { }

Blankets

Traits for any type that implements another trait

impl<T: Display> ToString for T {
    ...
}
let s = 3.to_string();

Testing

cargo test --help
cargo test -- --help
cargo test -- --test-threads=1
cargo test -- --show-output
cargo test <test>
cargo test --test <test-in-"tests"-dir>
cargo test -- --ignored
pub fn add(left: usize, right: usize) -> usize {
  left + right
}

#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn it_works() {
    let result = add(2, 2);
    assert_eq!(result, 4);
  }
  #[test]
  #[should_panic]
  fn it_fails() {
    ...
  }
  #[test]
  #[ignore]
  fn it_is_ignored() {
    ...
  }
}

Concurrency

use std::thread;
use std::time::Duration;

let handle = thread::spawn(|| {
  for i in 1..10 {
    println!("hi number {i} from the spawned thread!");
    thread::sleep(Duration::from_millis(1));
  }
});

for i in 1..5 {
  println!("hi number {i} from the main thread!");
  thread::sleep(Duration::from_millis(1));
}

handle.join().unwrap();
use std::sync::mpsc;
use std::thread;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
  let val = String::from("hi");
  tx.send(val).unwrap();
});

let received = rx.recv().unwrap();
println!("Got: {received}");

Pattern Matching

if let Some(x) = some_option_value {
  println!("{x}");
}
fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({x}, {y})");
}

let point = (3, 5);
print_coordinates(&point);
let x = 1;
match x {
  1 | 2 => println!("one or two"),
  3 => println!("three"),
  _ => println!("anything"),
}
let x = 5;
match x {
  1..=5 => println!("one through five"),
  _ => println!("something else"),
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
  Point { x, .. } => println!("x is {x}"),
}
let num = Some(4);
match num {
  Some(x) if x % 2 == 0 => println!("The number {x} is even"),  // a match guard
  Some(x) => println!("The number {x} is odd"),
  None => (),
}

Macros are a way of writing code that writes other code, which is known as metaprogramming.

#[macro_export]
macro_rules! vec {
  ( $( $x:expr ),* ) => {
    {
      let mut temp_vec = Vec::new();
      $(
        temp_vec.push($x);
      )*
      temp_vec
    }
  };
}

References

Clone this wiki locally