Memory Management and Ownership

Book Example: Iron-GCD

  • Turn the command-line app into a webapp

  • There are a lot of neat web frameworks in Rust

  • Performance is pretty good

Book Example: Mandelbrot

  • Shows use of external crate via module interface

  • Shows performance of Rust

  • Concurrency!

HW 1: Suggestions

  • Idea: Get rid of intermediate vector and just use iterators

  • Idea: Function pointers are first-class

  • Idea: Don't crash on error

Standard Memory Management

  • Programming without dynamic memory allocation and deallocation?

  • Two standard dynamic memory models

    • Automatic: Garbage collector or reference counting system

    • Garbage Collector: When low on memory, trace out all accessible memory, free non-accessible

    • Reference Counting: Keep track of how many references to a particular chunk of memory. When count goes to zero, free it

    • Manual: Programmer keeps track of which memory should be preserved, allocates new memory, frees old

Rust Memory Management

  • Invisible Manual: Compiler issues code to allocate memory and free memory where needed

  • This is restrictive: programmer must ensure that memory is not allocated too late or freed to early, in the presence of pointers

  • Rust compiler ensures that memory is allocated before use, statically unavailable at time of free

  • Key is lexical scope: when a variable statically leaves scope, its value is no longer reachable, so freed

  • Example

Memory Allocation

  • By default, a value must be allocated in memory unless it is small enough to fit in a register and marked copyable and bleah bleah bleah

  • Choices are stack allocation or heap allocation: default is stack

  • Heap allocation is ultimately done in unsafe code

  • Drop trait allows explicit actions during deallocation

Copyable Values

  • If a type has the Copy trait (e.g. the integer types) the compiler will feel free to make a copy of it whenever convenient

  • If a type has the Clone trait (e.g. most built-in types) the compiler will make a (deep) copy whenever the type's clone() method is called

  • Otherwise there will be no user-visible copying

Moves

  • The compiler may choose to insert code to move a thing to a different place in memory

  • If this happens, it will not make a copy: it will leave the old thing uninitialized and unreferenceable.

  • Example

Ownership

  • Net effect of all this: at any given time a value is "owned" by a particular name

  • The value is given to the owning name when it is created (Resource Acquisition Is Initialization = RAII)

  • The value is freed when the owning name leaves scope

  • Ownership can be transferred by a move

Lifetimes

  • Rust tracks ownership via explicit or implicit lifetime specifiers

  • Format is 'a, read "tick a"

  • Compiler's "borrow checker" tracks lifetime and ownership of values, throws a static error when can't work

  • Usually implicit, but can be made explicit when needed / wanted

      struct S<'a> {
          field: T<'a>,
      }
    

The Takeaway

  • Need to develop an operational mental model of ownership and lifetimes

  • When confused, refer to that model or get help

Last modified: Monday, 8 April 2019, 5:31 PM