Continuing on with my engineering notebook, here's what I've been learning about Rust recently. Mostly still following Beingessner's book, which is about memory management primitives in Rust.
The first thing, as I covered last time is that the book teaches Rust's peculiar (but excellent) flavor of memory management. Along the way, it teaches Rust's sum types (using enum
as its keyword), matching with completion, and Rust's maybe types (using Option<>
).
One thing it doesn't teach is about is tuple structs. I had to look them up; I keep encountering them in other people's code, but they weren't obvious to me and I couldn't remember seeing them in The Rust Programming Language, so I kept calling them "tuple types," but no, they're called tuple structs, and you access their internals via a dotted index:
struct Color(i32, i32, i32); // Red, Green, Blue let lightskyblue = Color(135, 206, 250); let amount_of_blue = lightskyblue.2;
Box<T>
: The thing with Rust is that you still have to care, a lot, about whether something is on the stack or the heap. Using the Box<T>
type automatically puts something on the heap, and is tracked in such a way that Rust deletes it automatically when the handle to it goes out of scope.
Rc<T>
is a wrapper around Box that allows for multiple handles to point to T
, counting the number of handles referring to T
, and automatically deleting T
when the count goes to zero.
Arc<T>
is just like Rc<T>
except that it also wraps the counter in a mutex handler and allows for multiple threads to reference the object safely.
RefCell<T>
allows one to borrow a referenced object from inside an Rc
or Arc
or other immutable container. This allows the user to mutate the contents of what would other be an immutable. When using RefCell<T>
, you must use .borrow()
or .borrow_mut()
to "dynamically borrow" and object that normally would not be borrow-able.
Rust continues to be... weird. This one in particular gets me:
<code>pub struct List<T> { head: Link<T>, }
type Link<T> = Option<Rc<Node<T>>>;
struct Node<T> { elem: T, next: Link<T>, }
pub struct Iter<'a, T:'a> { next: Option<&'a Node<T>>, }
// In body of impl<T> List<T>:
pub fn iter(&self) -> Iter<T> {
Iter { next: self.head.as_ref().map(|node| &**node) }
}</code>
I mean, I get that Rc
is a pointer, and as_ref
turns the head object into a reference to the head object, and map
only works if node is not None, so we have to dereference it twice, and then to make Rustc happy that we're saving a reference mark it as a reference to this twice-dereferenced thing, but... oy. That just looks ugly.
And yes, I get that I have to internalize all this before I can even begin to write Rust meaningfully.