Smart Pointers

  • Smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities.

  • Smart pointers in Rust often own the data they point to, while references only borrow data.

  • Box<T>: Handle recursive types, which the compiler cannot compute their size at compile-time.

  • Rc<T> (Reference Counted): Keeps track of how many references exist to the data and automatically cleans up when the reference count drops to zero.

  • Arc<T> (Atomic Reference Counted): Similar like RC but safe to be shared across threads.

  • Cow<T> (Clone On Write): provide immutable access to borrowed data, and clone the data lazily when mutation or ownership is required.

box1.rs

// At compile time, Rust needs to know how much space a type takes up. This
// becomes problematic for recursive types, where a value can have as part of
// itself another value of the same type. To get around the issue, we can use a
// `Box` - a smart pointer used to store data on the heap, which also allows us
// to wrap a recursive type.
//
// The recursive type we're implementing in this exercise is the "cons list", a
// data structure frequently found in functional programming languages. Each
// item in a cons list contains two elements: The value of the current item and
// the next item. The last item is a value called `Nil`.

// Use a `Box` in the enum definition to make the code compile.
#[derive(PartialEq, Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}

// Create an empty cons list.
fn create_empty_list() -> List {
    List::Nil
}

// Create a non-empty cons list.
fn create_non_empty_list() -> List {
    List::Cons(10, Box::new(List::Nil))
}

fn main() {
    println!("This is an empty cons list: {:?}", create_empty_list());
    println!(
        "This is a non-empty cons list: {:?}",
        create_non_empty_list(),
    );
}

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

    #[test]
    fn test_create_empty_list() {
        assert_eq!(create_empty_list(), List::Nil);
    }

    #[test]
    fn test_create_non_empty_list() {
        assert_ne!(create_empty_list(), create_non_empty_list());
    }
}
  • As the comment stated in this exercise, we need to use Box to wrap the recursive.

  • So we need to change the enum variant to Cons(i32, Box<List>).

  • Then complete the create function with List::Nill for empty cons list.

  • And List::Cons(10, Box::new(List::Nil)) for non-empty cons list.

rc1.rs

  • With Rc reference can be shared and have multiple owners.

  • In this case we only need to use Rc::clone(&sun) instead of creating new Sun.

  • And then properly drop the planet so the test case at the end will not fail.

arc1.rs

  • This exercise is straightforward, we need to use Arc.

  • First create shared_numbers using Arc::new(numbers).

  • Then inside th for block create child_numbers using Arc::clone(&shared_numbers).

cow1.rs

  • This exercise will simulate the Cow pattern.

    The type Cow is a smart pointer providing clone-on-write functionality: it can enclose and provide immutable access to borrowed data, and clone the data lazily when mutation or ownership is required.

  • We just need to put proper type match, either Cow::Owned(_) or Cow::Borrowed(_).

    • reference_no_mutation should be Cow::Borrowed(_).

    • owned_no_mutation should be Cow::Owned(_).

    • owned_mutation should be Cow::Owned(_).

Last updated