CMSC330

Rust

Rust

Borrow Checker
Ownership
References

Borrow Checker

Borrow Checker

Type Systems

Recall Type Systems: Rules linking type to constructs

Type Systems give meaning to constructs (bits)

Tells us two things:

  • What type is _
  • What can we do with type _
Type Systems
  • What can we do with type _

Will accept "good" programs


// Java
String x = "4" + 2.0;

// C
int x = *"4" + 2.0;

(* OCaml *)
let x = 4 + 20
          
Type Systems
  • What can we do with type _

Will reject "bad" programs


// Java
boolean x = "4" < 2.0;

// C
int x = "4" + 2.0;

(* OCaml *)
let x = 4 + 2.0
          
Type Systems

Will accept "good" programs

Will reject "bad" programs

How: Project 5 - Check types of AST

Type Systems

How: Project 5 - Check types of AST

Recall Ontological Problem

Exapnd Identity \(\Rightarrow\) Expand capabilities

Type Systems

Exapnd Identity \(\Rightarrow\) Expand capabilities

Rust: Check not just types, but more

The Problem

C is fast, but memory is unsafe

Java,OCaml,Python: slower but memory is safe

Why slow: Recall Garbage Collection

The Problem

Why slow: Recall Garbage Collection

In Particular: Heap

Stack is automatic:

  • Size is known
  • Lifetime is known
The Problem
  • Lifetime is known

Solve this problem: no GC needed

Ownership

Statically figure out when something should be dropped (freed)

Ownership Rules

  • Each value in Rust has an owner
  • There can only be one owner at a time
  • When the owner goes out of scope, the value will be dropped

(Important for heap values)

(Important for heap values)

Strings: values stored on the heap


let x = String::from("Hello"); //not a literal

let mut y = String::from("Hello"); //different from x
y.push_str(" world"); //modify y
          

Each value in Rust has an owner


let x = String::from("Hello"); // x owns "Hello"

let mut y = String::from("Hello"); // y has it's own version
y.push_str(" world");

println!("{x}"); // "Hello"
println!("{y}"); // "Hello World"
          

There can only be one owner at a time


let x = String::from("Hello"); // x owns "Hello"

let y = x; // ownership is MOVED from x to y

println!("{x}"); // ERROR
          

x becomes invalid

When the owner goes out of scope, the value will be dropped


{
  let x = String::from("Hello"); // x owns "Hello"
  println!("{}",x);
} // x is DROPPED
          

1 owner \(\Rightarrow\) no double free

1 owner \(\Rightarrow\) no double free

There can only be one owner at a time


{
  let x = String::from("Hello"); // x owns

  let y = x; // now y, x becomes invalid
} // drop y 
          

Strings will shallow copy by default

Need to track values through program flow


fn main() {
    let s = String::from("hello");  
    takes_ownership(s);
    println!("{s}"); //ERROR
} 

fn takes_ownership(some_string: String) { 
    println!("{}", some_string);
} 
          

Line 9: "hello" is dropped

Need to track values through program flow


fn main() {
    let s = String::from("hello");  
    let y = takes_returns_ownership(s);
    println!("{y}");
} 

fn takes_returns_ownership(some_string: String)->String { 
    println!("{}", some_string);
    some_string
} 
          

Line 5: "hello" is dropped

Need to track values through program flow

Issue with transfer


fn main() {
    let s = String::from("hello");  
    let l = takes_ownership(s);
    println!("{s} is {l} long"); //ERROR
} 

fn takes_ownership(some_string: String)-> usize { 
  some_string.len()
} 
          

Line 9: "hello" is dropped

Need to track values through program flow


fn main() {
    let s = String::from("hello");  
    let (y,l) = annoying(s);
    println!("{y} is {l} long");
} 

fn annoying(some_string: String)->(String,usize) { 
    let l = some_string.len();
    (some_string,l)
} 
          

Fix with references

References

Allows access to values without moving ownership


let x = String::from("Hello");
let y = &x; // x still owns "hello"
          

fn main() {
    let s = String::from("hello");  
    let l = annoying(&s);
    println!("{s} is {l} long");
} 

fn annoying(some_string: &String)->(usize) { 
    some_string.len()
} 
          

Allows access to values without moving ownership

Ownership cannot move while references exist


fn main() {
    let x = String::from("hello");  
    let y = &x; // borrow occurs here
    let z = x; // ERROR
    println!("{y}");
} 
          

Allows access to values without moving ownership

Ownership cannot move while references exist


fn main() {
    let s = String::from("hello");  
    let (y,l) = annoying(s);
    println!("{y} is {l} long");
} 

fn annoying(some_string: String)->(String,usize) { 
    //let l = some_string.len();
    //(some_string,l)
    (some_string,some_string.len()) // ERROR
} 
          

Struct's have a ref to self

Can make mutable references


let mut x = String::from("Hello");
let y = &mut x;

y.push_str(" world!");
println!("{x}"); // Hello world
          

Important: y is not mutable

Can make mutable references

Important: y is not mutable


let mut x = String::from("Hello");
let y = &mut x;

y.push_str(" world!");
println!("{x}"); // Hello world
          

y points to mutable data

Can make mutable references

Mutable Variable \(\not\Rightarrow\) Mutable refernce


let mut x = String::from("Hello");
let mut y = String::from("World");
          

let a = &x;         // immutable a borrows x immutably
let b = &mut x;     // immutable b borrow x mutably
let mut c = &x;     // mutable c borrows x immutably
let mut d = &mut x; // mutable d borrows x mutably
          

let mut x = String::from("Hello");
let mut y = String::from("World");
          

let a = &x;         // immutable a borrows x immutably

let b = &mut x;     // immutable b borrow x mutably
b.push_str("!");    // x = Hello!
b = &mut y;         // ERROR

let mut c = &x;     // mutable c borrows x immutably
c = &y;             // c now points to World 

let mut d = &mut x; // mutable d borrows x mutably
d.push_str("!");    // x is now Hello!!
d = &mut y;         // d now points to World
          

let mut x = String::from("Hello");
let mut y = String::from("World");
          

mut is now part of type


let mut a = &mut x;
a = &y; // ERROR - mismatched types: &mut string != &String
          

Rust: no data races \(\Rightarrow\) safety

Mutable references can cause data race

In other languages

Mutable references can cause data race

In other languages

Rust Type System prevents this with 2 rules:

  • All refs must be valid
  • 1 or the other, not both:
    • Any number of immutable refs
    • 1 mutable reference
  • All refs must be valid

ie. no dangling pointers!


fn dangle() -> &String {
    let s = String::from("hello");
    &s
}
          

Rust will not compile this

Lifetimes: future lecture

  • Any number of immutable refs
  • 1 mutable reference

let x = String::from("Hello");
let y = &x;
let z = &x;
          

let mut x = String::from("Hello"); // x is mut ref to Hello
let y = &mut x;                    // y is mut ref to Hello
println!("{x},{y}"); // ERROR
          
  • Any number of immutable refs
  • 1 mutable reference

let mut x = String::from("Hello"); // x is mut ref to Hello
let y = &mut x;                    // y is mut ref to Hello
println!("{x},{y}"); // ERROR
          

let mut x = String::from("Hello"); // x is mut ref to Hello
{
  let y = &mut x;                  // y is mut ref to Hello
  println!("{y}");
}                                  // y mut ref goes away
println!("{x}");                   // now we can use x
          

Update: scope of ref changed Fall 23 (Rust 1.62-2022)

Scope of ref is from when made to last used


let mut x = String::from("Hello"); 
let y = &mut x;
println!("{y}"); 
println!("{x}"); // OKAY               
          

let mut x = String::from("Hello"); 
let y = &mut x;
println!("{x}");                
println!("{y}"); // NOT OKAY