Stanford CS110L
Info
Study notes based on Stanford CS110L, Spring 2020 online videos.
Why Rust¶
- Why not C/C++?
- Memory safety issues
- Overflow: buffer overflow, integer overflow, etc.
- Valgrind is unable to find leaks resulted by buffer overflow.
- Memory safety issues
- Why not use GC'ed languages
- Expensive
- No matter what type of GC is used, there will always be nontrivial memory overhead.
- Disruptive
- Drop whatever you're doing - it's time for GC!
- Non-deterministic
- Nobody knows when will the next GC pause be. Depends on how much money is being used.
- Precludes manual optimization
- GC optimizes for the average use case, ignorant of knowing how you might use memory.
- Memory safety issues persist
- Expensive
Memory Safety¶
- Why is it so easy to screw up in C?
- Dangling pointers
- Double frees
- Iterator Invalidation
- Memory leaks
- It is incredibly hard to reason about programs.
- How does Rust prevent from the errors above?
- Ownership
- Passing ownership/references: just passes a pointer
- Borrowing
- Lifetimes
- Ownership
Error Handling¶
- Handling nulls
- Delete
NULL
and replace withNone
wrapped inOption
- Delete
- Handling errors
- Most languages use exceptions
- Failure mode are hard to spot: any function can throw any exception at any time
- Hard to manage in evolving codebases
- Especially hard when manual memory management is involved
- Error handling in Rust
- If an unrecoverable error occurs, panic
- If a recoverable error may occur, return a
Result
- Use
unwrap()
andexpect()
- Most languages use exceptions
Object Oriented Rust¶
Check out the official lecture notes.
Traits and Generics¶
- Traits
- What can traits do?
- Display
- Clone/Copy
- Iterator/IntoIterator
- Eq/PartialEq
- Allows us to override functionality
- Drop
- Deref
- Allows us to define default implementations
- ToString
- Allows us to overload operators
- What can traits do?
- Generics
- No performance impact at runtime: compiler create separate functions for types that are used
Smart Pointers¶
Box<T>
- Plays the same role as
unique_ptr<T>
in C++
- Plays the same role as
Rc<T>
- Allows us to have multiple immutable references to a chunk of heap memory
- Caution: you can have memory leaks if you create reference cycles
RefCell<T>
- Allows us to have shared references to the cell with mutability
- Its
new
function doesn't heap allocate - This is still safe because it will enforce the reference rules at runtime, which results in additional cost
- Common pattern:
Rc<RefCell<T>>
Pitfalls in Multiprocessing¶
- Don't call
fork()
- Why fork?
- Get concurrent execution
- Accidentally nesting forks when spawning multiple child processes
- Children can execute code they weren't supposed to
- Accessing data structure during threading
- Failure to clean up zombie children if
waitpid()
isn't called
- Invoke external functionality on the system
- Almost every
fork()
is followed by anexec()
- Almost every
- Get concurrent execution
- Common multiprocessing tactic
- Let
fork()
andexec()
be - Define a higher-level abstraction to take care of the common cases
- Let
Command
in Rust- No concurrency: run, and get the output in a buffer
- No concurrency: run (without swallowing output), and get the status code
- With concurrency: spawn and immediately return
pre_exec()
function
- Why fork?
- Don't call
pipe()
- Problems
- Leaked file descriptors
- Use-after-close
- Potential solution
- Add a layer of abstraction
- Write to a stdin pipe (what rust does)
- The
os_pipe
crate allows for creating arbitrary pipes
- The
- Problems
Google Chrome¶
- Don't call
signal()
signal()
is dead. Long livesigaction()
- The only portable use of
signal()
is to set a signal's disposition toSIG_DFL
orSIG_IGN
- The only portable use of