🦉
Programming Notes
  • My Programming Notes
  • CKA Exam Preparation
    • Certified Kubernetes Administrator
    • Setup Minikube
    • Network Design Principles
    • Role-Based Access Control (RBAC)
    • Namespace
    • Resource Quota
    • Pod
    • Deployment
    • Deployment: Rollout
    • ConfigMap
    • Service
    • Service: kubectl expose
    • Pod: Resources Management
    • Pod & Container: Quality of Service Class
    • Pod & Container: Probes
    • Limit Range
    • Scaling: Manual
    • Scaling: Horizontal Pod Autoscaler
    • Persistent Volume & Claim
    • Secret
    • Ingress: Routing
    • Ingress: TLS
    • Ingress: Rate Limit
    • Ingress: Basic Auth
    • Ingress: CRD (Custom Resource Definition)
    • Job
    • CronJob
    • Mutli-Node Cluster
  • Golang
    • Generics
    • Context
    • Goroutines and Channels in Go
    • Goroutine: Concurrency vs Parallelism
    • Goroutine: Performance & Tradeoffs
    • JSON: omitzero
  • Rust
    • Arrays & Slices
    • Closures
    • Generics & Traits
    • Iterators
    • Run Code Simultaneously
    • String vs &str
    • Tests
    • Rustlings Exercises
      • Variables
      • Functions
      • If
      • Primitive Types
      • Vectors
      • Move Semantics
      • Structs
      • Enums and Matching Pattern
      • Strings
      • Modules
      • Hashmaps
      • Options
      • Error Handling
      • Generics
      • Traits
      • Lifetimes
      • Tests
      • Iterators
      • Smart Pointers
      • Threads
      • Macros
      • Quiz 1
      • Quiz 2
      • Quiz 3
  • Software Engineering
    • CAP Theorem
    • Circuit Breaker
    • Decoupling
    • GraphQL: Query Caching
    • HMAC Signature
    • Idempotency
    • Monolith VS Microservice
    • OWASP Top 10 2021
    • PCI DSS
    • PostgreSQL: Partitioning
    • PostgreSQL: Replication
    • Protobuf & gRPC
    • Redis: Streams
    • Resource Scaling
    • Signed URL
    • SOLID
    • Stack VS Heap
    • Stateful VS Stateless
  • Site Reliability Engineering
    • Chaos Engineering
    • Distributed Tracing
    • Kubernetes (k8s)
    • SLA, SLO, and SLI Metrics
    • Site Reliability Engineer
  • Others
    • FFMPEG Cheat sheet
Powered by GitBook
On this page
  • move_semantics1.rs
  • move_semantics2.rs
  • move_semantics3.rs
  • move_semantics4.rs
  • move_semantics5.rs
  1. Rust
  2. Rustlings Exercises

Move Semantics

PreviousVectorsNextStructs

Last updated 4 months ago

  • Each value in Rust has an owner.

  • There can only be one owner at a time.

  • When the owner goes out of scope, the value will be dropped.

  • When assigning a value to another variable or passing it to a function, ownership is transferred (moved).

  • Borrowing lets you access a value without transferring ownership, using references (&).

  • You can have only one mutable reference or any number of immutable references at a time.

  • References:

move_semantics1.rs

// TODO: Fix the compiler error in this function.
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
    let mut vec = vec;

    vec.push(88);

    vec
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn move_semantics1() {
        let vec0 = vec![22, 44, 66];
        let vec1 = fill_vec(vec0);
        assert_eq!(vec1, vec![22, 44, 66, 88]);
    }
}
  • In this exercise the function fill_vec tried to change vec variables.

  • But it got compile error because vec is not mutable.

  • When passing vec0 into function fill_vec it also transfer or move the ownership from main function to fill_vec function.

  • So fill_vec as the owner can do anything with it including change the mutability.

  • Then we can easily add mutable in here let mut vec = vec; to fix the code.

  • And that makes vec is mutable and we can push value into it.

move_semantics2.rs

fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
    let mut vec = vec;

    vec.push(88);

    vec
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to
    // fix the compiler error in the test.
    #[test]
    fn move_semantics2() {
        let vec0 = vec![22, 44, 66];

        let vec1 = fill_vec(vec0.clone()); // pass cloned vec0

        assert_eq!(vec0, [22, 44, 66]);
        assert_eq!(vec1, [22, 44, 66, 88]);
    }
}
  • In this exercise the task is to make both vec0 and vec1 accessible at the same time.

  • If we pass vec0 to function fill_vec it will transfer or move the ownership.

  • That make the vec0 variable invalidated and we got error:

    borrow of moved value: `vec0`
  • So instead of passing vec0 we pass a clone of vec0.

  • That makes both vec0 and vec1 valid.

move_semantics3.rs

// TODO: Fix the compiler error in the function without adding any new line.
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> { 
    //      ^^^ add mut
    vec.push(88);

    vec
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn move_semantics3() {
        let vec0 = vec![22, 44, 66];
        let vec1 = fill_vec(vec0);
        assert_eq!(vec1, [22, 44, 66, 88]);
    }
}
  • This exercise is similar with move_semantics1.rs.

  • But instead of changing the mutability by redeclare variables with mut we do it inside the function parameters.

move_semantics4.rs

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    // TODO: Fix the compiler errors only by reordering the lines in the test.
    // Don't add, change or remove any line.
    #[test]
    fn move_semantics4() {
        let mut x = Vec::new();
        let y = &mut x;
        y.push(42); // move this line above z
        let z = &mut x;
        z.push(13);
        assert_eq!(x, [42, 13]);
    }
}
  • You can have only one mutable reference or any number of immutable references at a time.

  • So the original code have compile error because of x have more than one mutable reference.

    error[E0499]: cannot borrow `x` as mutable more than once at a time
      --> exercises/06_move_semantics/move_semantics4.rs:13:17
       |
    12 |         let y = &mut x;
       |                 ------ first mutable borrow occurs here
    13 |         let z = &mut x;
       |                 ^^^^^^ second mutable borrow occurs here
    14 |         y.push(42);
       |         - first borrow later used here
  • By simply moving y.push(42); above let z = &mut x; we can fix it.

  • Because y already done with the push so x is free and can be borrowed by z in the next step.

move_semantics5.rs

#![allow(clippy::ptr_arg)]

// TODO: Fix the compiler errors without changing anything except adding or
// removing references (the character `&`).

// Shouldn't take ownership
// add & to borrow instead
fn get_char(data: &String) -> char {
    data.chars().last().unwrap()
}

// Should take ownership
fn string_uppercase(mut data: String) {
    data = data.to_uppercase();

    println!("{data}");
}

fn main() {
    let data = "Rust is great!".to_string();

    get_char(&data);

    string_uppercase(data);
}
  • If we look at the comment the function string_uppercase it shouldn't take ownership but the original code does it.

  • So we just need to add reference data: &String to the parameter and pass &data when calling get_char.

  • That makes it borrow instead of move.

  • For string_uppercase remove the reference from data parameters because we want to move the ownership to it and remove the reference too when calling it.

Ownership
Reference and borrowing