Skip to content

Mutability propagation through alias info

Andreea Costea edited this page Jun 15, 2023 · 4 revisions

Consider the function test_A.

fn test_A(){
  let mut x = String::new();              
  let a: &mut String = &mut x;
  let b: &mut String = a;
  println!("b: {}", b); 
  let c = b;
  println!("c: {}", c); 
}

Let's assume we would like to refactor its last three lines as follows:

fn foo_A(b: &String){
  println!("b: {}", b); 
  let c = b;
  println!("c: {}", c);       
}

fn test_A(){
  let mut x = String::new();              
  let a: &mut String = &mut x;
  let b: &mut String = a;
  foo_A(b);
}

Note that, although b is a mutable reference in test_A, foo_A only requires an immutable borrow since b and its alias c are only used in a read-only manner.

Consider next test_B, a variation of the above function, where c is used to mutate the value it refers to.

fn test_B(){
  let mut x = String::new();              
  let a: &mut String = &mut x;
  let b: &mut String = a;
  println!("b: {}", b); 
  let c = b;
  c.push('a');
} 

After extracting the last three lines in foo_B as below, our analysis collects a constraint system mut <: c to denoted that c requires mutability capabilities. Although there is no mutability constraint collected for b, since c and b are aliased, the mutability constraint is being propagated to b as well resulting in the following refactored code:

fn foo_B(b:&mut String){
  println!("b: {}", b); 
  let c = b;
  c.push('a'); 
}

fn test_B(){
  let mut x = String::new();              
  let a: &mut String = &mut x;
  let b: &mut String = a;
  foo_B(b);
}