Rust Trivia

What are the 3 types used to represent a sequence of values, and what are their generic type annotations?


  1. Array [T; N]
  2. Vector Vec<T>
  3. Slice &[T]

How do you check the current size and capacity of any sequential-type value?


.len() and .capacity()

What makes a String and a &str unique? What are their effective underlying types?


  1. A String is just a Vec<u8> with the guarantee that the data is well-formed UTF-8.
  2. A &str is just a slice &[u8] of a String.

Given let x = y;, under what condition would it be true that y did not become uninitialized?


If the type of y implements the Copy trait.

Given let s = "hello!";, what is the type of s?


s is a &str whose pointer refers to preallocated, read-only memory on the stack.

How do you get the size of any data type?


std::mem::size_of::<T>();

What's a fat pointer?


A fat pointer is a pointer to a slice (a region of an array or vector). It's a two-word value on the stack comprised of:

  1. A pointer to the slice's first element
  2. The number of elements in the slice

What's the difference between Arc and Rc types?


Arc (atomic reference count) is safe to share between threads directly, whereas a Rc uses faster non-thread-safe code to update its reference count.

When defining a type (struct), when is it required that a field's lifetime be specified?


Lifetimes must be specified when a field is a reference type. e.g.

struct RefPoint<'a, 'b> {
  x: &'a f64,
  y: &'b f64,
}

What risk is sometimes posed by using reference count types?


If two Rc types point to each other, they will keep each other's ref count above zero and neither will be freed. This is called a reference cycle.

Given two reference variables x and y, how do I check to see if they point to the same value in memory?


std::ptr::eq(x, y)

What are the two ways for closures to get data from enclosing scopes?


  1. Moves
  2. Borrowing

What are the three variants of IntoIterator implementations?


  1. Shared reference
  2. Mutable reference
  3. By value

When should you use either Path or OsStr?


For absolute and relative paths, use Path. For an individual component of a path, use OsStr.

How are bool values stored in memory, and why?


bool values are stored as a whole byte so that pointers to them may be created.

What mechanism should you reach for to allow for shared ownership of a value?


Rc<T> or Arc<T> (if sharing across multiple threads)

What mechanism allows us to mutate the value inside of an Rc<T>? What about an Arc<T>?


For an Rc<T>, interior mutability can be facilitated by a RefCell<T>. For an Arc<T>, you'd reach for a Mutex<T>.

What special ability does a Pin'd object have?


Pinned (i.e. immovable) objects can have pointers to their own fields. e.g.


#![allow(unused)]
fn main() {
struct MyFuture {
  a: i32,
  ptr_to_a: *const i32, // I point to my own `a`
}
}

When would you use ArcWake (from the futures crate) trait?


Use ArcWake when you need an easy way to construct a Waker.

What is the actual return type of this function?


#![allow(unused)]
fn main() {
async fn get_five() -> u8 { 5 }
}

Returns value of type impl Future<Output = u8>.

How is an async function in terms of lifetimes if one of its arguments is a > reference or non-'static value?


Unlike regular functions, async functions whose parameters are references or non-'static return a Future which is bounded by the lifetime of the arguments. Meaning, the future returned from an async fn must be .awaited while its non-'static arguments are still valid.

What's the workaround for async functions' non-static lifetime rules?


An async function's Future return value can be make 'static by moving the non-static (or reference) values into an async block:


#![allow(unused)]
fn main() {
fn work_around() -> impl Future<Output = u8> {
  async {
    let x = 5;
    borrow_x(&x).await
  }
}
}

What's the formal fancy term to describe Rust's form of polymorphism?


Bounded parametric polymorphism.

What's the pattern used as a way to get around the orphan rule?


The newtype pattern, which involves creating a new type in a tuple struct.

I've implemented a newtype, Wrapper, that wraps a Vec<T>, but now I can't use the Vec<T>'s built-in methods! What can I do?


Implement the Deref trait for Wrapper, which would allow us to treat Wrapper exactly like a Vec.

Something about how passing by value cedes all ownership of a value. Use drop as point of reference.

How do I write an impl T function that consumes T (it will no longer be usable by others) and converts it to U?


In the function's signature, you'd have self based by value, which will consume it. (usually, impl functions receive &self)

How do I get the address of a value (say, a String)?



#![allow(unused)]
fn main() {
let txt = String::from("hello world");
let txt_stack = &txt as *const _; // Address of pointer on the stack
let txt_heap = &txt.as_bytes()[0] as *const _; // Address of first charcacter in heap
dbg!((txt_stack, txt_heap));
}

TODO: Go here and add stuff about the use of phantom/types/data