main.rsCourse

Rust

A typed name, then a struct with a method, then why a second name needs clone, a method that changes the struct, an enum, and a trait.

A cup label is three typed values
  1. name&strlatte
  2. centsi32450
  3. readybooltrue

6 lessons, in orderFree to read

Syllabus

Basics

The types, explained, then an object you can name and call.

  1. A Rust name has a type. name is a string slice, cents is an i32, and ready is a bool. The program prints latte $4.50 ready.

    fn main() {
        let name: &str = "latte";
        let cents: i32 = 450;
        let ready: bool = true;
        let status = if ready { "ready" } else { "sold out" };
        println!("{name} ${}.{:02} {status}", cents / 100, cents % 100);
    }
    latte $4.50 ready
    
  2. A struct groups fields, and impl adds a method. Item holds a drink and a price in cents. label prints latte $4.50.

    Builds on lesson 1.

    struct Item {
        name: String,
        cents: i32,
    }
    
    impl Item {
        fn label(&self) -> String {
            format!("{} ${}.{:02}", self.name, self.cents / 100, self.cents % 100)
        }
    }
    
    fn main() {
        let latte = Item {
            name: String::from("latte"),
            cents: 450,
        };
        println!("{}", latte.label());
    }
    latte $4.50
    

Practice

A rule that prevents a bug, then a program that uses the object.

  1. Assigning a String moves it. The old name cannot use it. clone keeps both. The program prints latte latte.

    Builds on lessons 1 through 2.

    fn main() {
        let a = String::from("latte");
        let b = a.clone();
        println!("{a} {b}");
    }
    latte latte
    
  2. A method with &mut self changes the struct. add_tip adds 20 percent to a $4.50 bill, and the program prints $5.40.

    Builds on lessons 1 through 3.

    struct Bill {
        cents: i32,
    }
    
    impl Bill {
        fn add_tip(&mut self, percent: i32) {
            self.cents += self.cents * percent / 100;
        }
    }
    
    fn main() {
        let mut bill = Bill { cents: 450 };
        bill.add_tip(20);
        println!("${}.{:02}", bill.cents / 100, bill.cents % 100);
    }
    $5.40
    

Further

One more idea, then a program that puts the pieces together.

  1. An enum lists the shapes a value can have. Paid is Yes with the cents, or No. The program prints paid 450 and unpaid.

    Builds on lessons 1 through 4.

    enum Paid {
        Yes(i32),
        No,
    }
    
    fn read(a: Paid) -> String {
        match a {
            Paid::Yes(cents) => format!("paid {cents}"),
            Paid::No => "unpaid".to_string(),
        }
    }
    
    fn main() {
        println!("{}", read(Paid::Yes(450)));
        println!("{}", read(Paid::No));
    }
    paid 450
    unpaid
    
  2. A trait names the methods a function needs. Label asks for label. Item implements it. show prints latte $4.50.

    Builds on lessons 1 through 5.

    struct Item {
        name: String,
        cents: i32,
    }
    
    trait Label {
        fn label(&self) -> String;
    }
    
    impl Label for Item {
        fn label(&self) -> String {
            format!("{} ${}.{:02}", self.name, self.cents / 100, self.cents % 100)
        }
    }
    
    fn show(item: &impl Label) {
        println!("{}", item.label());
    }
    
    fn main() {
        let latte = Item { name: String::from("latte"), cents: 450 };
        show(&latte);
    }
    latte $4.50
    

Readings

Shorter pieces in the same language. Any order is fine once the lessons feel familiar.

  1. The borrow checker: many readers or one writer 2 min

    GoByte Skills #12: Rust lets you hold many shared references or one mutable one, never both. That single rule is why push on a borrowed Vec does not compile, and why two threads cannot write the same variable without a lock or an atomic.

  2. Ownership: one owner, no garbage collector 2 min

    GoByte Skills #11: Rust frees memory without a garbage collector because every value has exactly one owner. Move it and the old name is dead. When the owner's scope ends, the compiler has already written the drop call.

  3. The ? operator: errors are values 3 min

    GoByte Skills #10: The ? operator is a match you did not have to type. On Err it converts the error through From and returns early, so the error changes type on the way up and never leaves the signature. No exceptions, no unwinding.