Understanding Rust...finally

Plan of Action

  1. Core Concepts
  2. Ownership
  3. Borrowing
  4. Lifetimes
  5. Enums
  6. Handling Errors
  7. Iterator
  8. Advanced Lifetimes
  9. Generics
  10. Traits

1. Core Concepts

2 Ownership

  • Every value is "owned" by a single variable, struct, vector, etc. at a time.
  • Reassigning the value to another variable, passing it to a function, or putting it into a vector moves the value. The old variable can't be used anymore!

#[derive(Debug)]
struct Student {

    id: u32,
    name: String,
    score: i32,
}

impl Student {
    // Associated fn to create new student
    fn new_student(id: u32, name: String) -> Self {
        Student {
            id: id,
            name: name,
            score: 0,
        }
    }
}

#[derive(Debug)]
struct School {
    students: Vec<Student>,
}

impl School {
    // Associated fn to create new school
    fn new_school() -> Self {
        School {students: vec![]}
    }
}

Example #1

fn print_student (student: Student) {
    println!("{:#?}", student);
}

fn main() {
    let school = School::new_school();
    let other_school = school;
    println!("{:#?}", school);
}
error[E0382]: borrow of moved value: `school`
  --> src/test.rs:53:23
   |
51 |     let school = School::new_school();
   |         ------ move occurs because `school` has type `School`, which does not implement the `Copy` trait
52 |     let other_school = school;
   |                        ------ value moved here
53 |     println!("{:#?}", school);
   |                       ^^^^^^ value borrowed here after move
   |

Example #2

fn print_student (student: Student) {
    println!("{:#?}", student);
}

fn main() {
    let new_student = Student::new_student(7, String::from("Joe"));
    print_student(new_student);
    print_student(new_student);
}
error[E0382]: use of moved value: `new_student`
  --> src/test.rs:53:19
   |
51 |     let new_student = Student::new_student(7, String::from("Joe"));
   |         ----------- move occurs because `new_student` has type `Student`, which does not implement the `Copy` trait
52 |     print_student(new_student);
   |                   ----------- value moved here
53 |     print_student(new_student);
   |                   ^^^^^^^^^^^ value used here after move
   |

Example #3

fn print_student (student: Student) {
    println!("{:#?}", student);
}


fn main() {
    let student = Student::new_student(7, String::from("Joe"));
    let list_of_students = vec![student];
    print_student(student);
}
error[E0382]: use of moved value: `student`
  --> src/test.rs:53:19
   |
51 |     let student = Student::new_student(7, String::from("Joe"));
   |         ------- move occurs because `student` has type `Student`, which does not implement the `Copy` trait
52 |     let list_of_students = vec![student];
   |                                 ------- value moved here
53 |     print_student(student);
   |                   ^^^^^^^ value used here after move
   |

Example #4

fn main() {
    let school = School::new_school();
    let students = school.students;
    println!("{:#?}", school.students);
}
error[E0382]: borrow of moved value: `school.students`
  --> src/test.rs:53:23
   |
52 |     let students = school.students;
   |                    --------------- value moved here
53 |     println!("{:#?}", school.students);
   |                       ^^^^^^^^^^^^^^^ value borrowed here after move

Example #5

fn print_student (student: Student) {
    println!("{:#?}", student);
}


fn main() {
    let student = Student::new_student(7, String::from("Joe"));
    print_student(student);
    println!("{:#?}", student.name);
}
error[E0382]: borrow of moved value: `student`
  --> src/test.rs:54:23
   |
52 |     let student = Student::new_student(7, String::from("Joe"));
   |         ------- move occurs because `student` has type `Student`, which does not implement the `Copy` trait
53 |     print_student(student);
   |                   ------- value moved here
54 |     println!("{:#?}", student.name);
   |                       ^^^^^^^^^^^^ value borrowed here after move

Example #6

fn print_student (student: Student) {
    println!("{:#?}", student);
}

fn print_name(name: String) {
    println!("{:#?}", name);
}

fn main() {
    let student = Student::new_student(7, String::from("Joe"));
    print_name(student.name);
    print_student(student);
}
error[E0382]: use of partially moved value: `student`
  --> src/test.rs:54:19
   |
53 |     print_name(student.name);
   |                ------------ value partially moved here
54 |     print_student(student);
   |                   ^^^^^^^ value used here after partial move
   |
   = note: partial move occurs because `student.name` has type `String`, which does not implement the `Copy` trait

3 Borrowing

"Workaround"

fn print_student(student: Student) -> Student {
    println!("{:#?}", student);
    student //implicit return
}

fn main() {
    let mut student = Student::new_student(7, String::from("Joe"));
    student = print_student(student);
    student = print_student(student);
    println!("{:?}", student);
}

3.1 Borrowing - Immutable References

  • You can create many read-only references to a value that exist at the same time.
  • You can't move a value while a reference to the value exists.
fn print_student(student: &Student) {
    println!("{:#?}", student);
}

fn main() {
    let student = Student::new_student(7, String::from("Joe"));
    print_student(&student);
    println!("{:?}", student);
}

Here, we see the & operator being used on a type. This means the arguments needs to be a reference to a value.

fn print_student(student: &Student) {

& operator being used on an owner of a value. this mens we wil create a reference to this value.

print_student(&student);

Rule no 4.

fn print_student(student: &Student) {
    println!("{:#?}", student);
}

fn main() {
    let student = Student::new_student(7, String::from("Joe"));
    
    print_student(&student);      // First immutable borrow
    print_student(&student);      // Second immutable borrow

    let other_student = student;  // Ownership of 'student' is moved to 'other_student'
    println!("{:?}", student);    // Error: 'student' has been moved and can no longer be used
}
error[E0382]: borrow of moved value: `student`
  --> src/test.rs:63:22
   |
57 |     let student = Student::new_student(7, String::from("Joe"));
   |         ------- move occurs because `student` has type `Student`, which does not implement the `Copy` trait
...
62 |     let other_student = student;
   |                         ------- value moved here
63 |     println!("{:?}", student);
   |                      ^^^^^^^ value borrowed here after move
   |

3.2 Borrowing - Mutable References

  • You can make a writable (mutable) reference to a value only if there are no read-only references currently in use. One mutable reference to a value can exist at a time.
  • You can't mutate a value through the owner when any reference (mutable or immutable) to the value exists.
  • Some types of values are copied instead of moved (numbers, booleans, characters, arrays/tuples with copyable elements).
fn change_score (student: &mut Student) {
    student.score = 100;
}

fn main() {
    let mut student = Student::new_student(7, String::from("Joe"));
    change_score(&mut student);
    println!("{:#?}", student);
}

Not to do:

fn change_score (student: &mut Student) {
    student.score = 100;
}

fn main() {
    let mut student = Student::new_student(7, String::from("Joe"));
    let student_ref = &student;
    change_score(&mut student);
    println!("{:#?}", student_ref.name);
}
error[E0502]: cannot borrow `student` as mutable because it is also borrowed as immutable
  --> src/test.rs:49:18
   |
48 |     let student_ref = &student;
   |                       -------- immutable borrow occurs here
49 |     change_score(&mut student);
   |                  ^^^^^^^^^^^^ mutable borrow occurs here
50 |     println!("{:#?}", student_ref.name);
   |                       ---------------- immutable borrow later used here
fn change_score (student: &mut Student) {
    student.score = 100;
}

fn main() {
    let mut student = Student::new_student(7, String::from("Joe"));
    let student_ref = &mut student;
    change_score(&mut student);
    println!("{:#?}", student_ref.name);
}
error[E0499]: cannot borrow `student` as mutable more than once at a time
  --> src/test.rs:49:18
   |
48 |     let student_ref = &mut student;
   |                       ------------ first mutable borrow occurs here
49 |     change_score(&mut student);
   |                  ^^^^^^^^^^^^ second mutable borrow occurs here
50 |     println!("{:#?}", student_ref.name);
   |                       ---------------- first borrow later used here
fn change_score (student: &mut Student) {
    student.score = 100;
}

fn main() {
    let mut student = Student::new_student(7, String::from("Joe"));
    let student_ref = &mut student;
    student.score = 100;
    change_score(student_ref);
    println!("{:#?}", student);
}
error[E0506]: cannot assign to `student.score` because it is borrowed
  --> src/test.rs:49:5
   |
48 |     let student_ref = &mut student;
   |                       ------------ `student.score` is borrowed here
49 |     student.score = 100;
   |     ^^^^^^^^^^^^^^^^^^^ `student.score` is assigned to here but it was already borrowed
50 |     change_score(student_ref);
   |                  ----------- borrow later used here

To do:

fn add_student(school: &mut School, student: Student) {
    school.students.push(student);
}

fn main() {
    let mut school = School::new_school();
    let student = Student::new_student(7, String::from("Joe"));
    add_student(&mut school, student);
    println!("{:#?}", school);
    println!("{:#?}", school.students[0]); // print 'student' value
}
School {
    students: [
        Student {
            id: 7,
            name: "Joe",
            score: 0,
        },
    ],
}


Student {
    id: 7,
    name: "Joe",
    score: 0,
}

4. Lifetimes

  • When a variable goes out of scope, the value owned by it is dropped (cleaned up in memory).
  • Values can't be dropped if there are still active references to them.
  • References to a value can't outlive the value they refer to.
fn print_score(student: &Student) {
    println!("{}", student.score);
}

fn main() {
    let mut school = School::new_school();
    let student = Student::new_student(1, String::from("me"));

    let student_ref = &student; // Immutable reference to 'account'
 
    school.students.push(student); // Error: 'student' is moved here

    print_score(student_ref); // Error: Cannot use 'student_ref' after 'student' has been moved
}
fn print_score(student: &Student) {
    println!("{}", student.score);
}

fn main() {
    let mut school = School::new_school();
    let student = Student::new_student(1, String::from("me"));

    let student_ref = &student; // Immutable reference to 'student'
 
    print_score(student_ref); // Borrowing 'student' as immutable for printing its score
 
    school.students.push(student); // 'student' is moved into 'school.accounts'
}

Storing a value --> Take ownership (receive a value) Calculation with a value --> Read-only ref (&) Change a value --> Mutable Ref (&mut)


5. Enums


6. Handling Errors


References

https://smallcultfollowing.com/babysteps/blog/2016/04/27/non-lexical-lifetimes-introduction/ https://www.figma.com/blog/rust-in-production-at-figma/ https://www.rykap.com/2020/09/23/distance-fields/