CS3210 Design Operating Systems

Taesoo Kim





Rust 6: Smart Pointer and Container

Taesoo Kim

Administrivia

Administrivia

Q&A

impl<T: ?Sized> Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self // Q?
    }
}

Ref. “Rust Tidbits: Box Is Special”

Q&A

Can you please go over the difference between " impl thing for &StackVec " and “impl thing for StackVec”?

What is the most important contribution that rust has given the academic community? What companies hire for rust and for what kind of systems?

Ref. Why Discord is switching from Go to Rust

About Discord’s story

About Discord’s story

About Discord’s story

Agenda

Smart Pointers in C++

// overriding pointer semantics
T & operator * () { ... }
T * operator -> () { ... } 

std::unique_ptr<T>; // in charge of deallocation
std::shared_ptr<T>; // RC-based ownership (never freed when holding a ptr)
std::weak_ptr<T>;   // weak ownership (can be freed while holding a ptr)

Smart Pointers in Rust

Smart Pointers in Rust

let s = String::from("Hello");
*s; // <- Q. what does it mean by?

let b = Box::new("Hello");
*b; // <- Q. what does it mean by?

let r = Rc::new("Hello");
*r; // <- Q. what does it mean by?

Smart Pointers in Rust

Deref

Deref coercion

v = *x;     // v = *Deref::deref(&x)
*x = v;     // *DerefMut::deref_mut(&mut x) = v

x.func();   // x -> Deref::deref(&x) if func() exists
func(&x);   // &x -> Deref::deref(&x) if func accepts

Example: String

// @liballoc/string.rs
impl ops::Deref for String {
    type Target = str;

    fn deref(&self) -> &str { .. }
}

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

// str::to_uppercase() -> String
s.to_uppercase(); // String -> &str via deref()

Rc: reference counted pointer

let r1 = Rc::new(String::from("hello"));
println!("{}", r1);

// both r1/r2 points (i.e., behaves as if it owns) to String
let r2 = Rc::clone(&r1);
println!("{}", r1);
println!("{}", r2);

Example: Rc

let gadget_owner: Rc<Owner> = Rc::new( // rc++
  Owner {
    name: "Gadget Man".to_string(),
  }
);

let gadget1 = Gadget {
  id: 1,
  owner: Rc::clone(&gadget_owner),     // rc++
};
let gadget2 = Gadget {
  id: 2,
  owner: Rc::clone(&gadget_owner),     // rc++
};

drop(gadget_owner);                    // rc--, but no free!

vs. Reference

vs. Reference

vs. Reference

vs. Reference

vs. Reference

vs. Reference

Example: rc

// Q1. ?Sized?
pub struct Rc<T: ?Sized> {
    ptr: NonNull<RcBox<T>>,
    phantom: PhantomData<T>, // Q2. PhantomData?
}

PhantomData

Ref. Nomicon: 3.10. PhantomData

Example: PhantomData (unused lifetime param)

struct Slice<'a, T> {
  start: *const T,
  end: *const T,
}

struct Slice<'a, T: 'a> {
  start: *const T,
  end: *const T,
  phantom: PhantomData<&'a T>,
}

Example: rc::new()

pub fn new(value: T) -> Rc<T> {
  Rc {
    ptr: Box::into_raw_non_null(box RcBox {
                strong: Cell::new(1),
                weak: Cell::new(1),
                value,
    }),
    phantom: PhantomData,
  }
}

Let’s implement Box

One difference: Move a value out of a box

let x = Box::new(vec![1,2,3,4]);
let y = *x; // moves the vec out into `y`, then deallocates the box
            // but does not call a destructor on the vec

Ref. “Rust Tidbits: Box Is Special”

References