4.2. 참조자와 빌림

  • 참조자를 이용하여 소유권 이동 막기
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() }
 
 
notion imagenotion image
  • 참조자가 스포크 밖으로 나와도 메모리가 반납되지 않음.
fn calculate_length(s: &String) -> usize { // s는 String의 참조자입니다 s.len() } // 여기서 s는 스코프 밖으로 벗어났습니다. 하지만 가리키고 있는 값에 대한 소유권이 없기 // 때문에, 아무런 일도 발생하지 않습니다.
 
  • 빌림(borrowing): 함수의 매개변수로 참조자를 만드는 것. 소유권을 넘기지 않으면서 참조하는 것.
 

가변 참조자(mutable refeences)

fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
 
  • 가변 참조자는 특정 스코프 안에서 딱 한 번만 만들 수 있다.
let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s;
error[E0499]: cannot borrow `s` as mutable more than once at a time --> borrow_twice.rs:5:19 | 4 | let r1 = &mut s; | - first mutable borrow occurs here 5 | let r2 = &mut s; | ^ second mutable borrow occurs here 6 | } | - first borrow ends here
 
  • 이러한 제한은 데이터 레이스(data race)를 방지하기 위한 조치
데이터 레이스의 조건
  1. 두 개 이상의 포인터가 동시에 같은 데이터에 접근한다.
  1. 그 중 적어도 하나의 포인터가 데이터를 쓴다.
  1. 데이터에 접근하는데 동기화를 하는 어떠한 메커니즘도 없다.
 
 
  • 두 개 이상의 가변 참조자를 써야 할 때는 스코프를 새로 만든다.
let mut s = String::from("hello"); { let r1 = &mut s; } // 여기서 r1은 스코프 밖으로 벗어났으므로, 우리는 아무 문제 없이 새로운 참조자를 만들 수 있습니다. let r2 = &mut s;
 
  • 불변 참조자는 여러 번 만들 수 있으나 가변 참조자는 불변 참조자가 한 번이라도 만들어진다면 추가로 생성할 수 있다.
let mut s = String::from("hello"); let r1 = &s; // 문제 없음 let r2 = &s; // 문제 없음 let r3 = &mut s; // 큰 문제
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> borrow_thrice.rs:6:19 | 4 | let r1 = &s; // 문제 없음 | - immutable borrow occurs here 5 | let r2 = &s; // 문제 없음 6 | let r3 = &mut s; // 큰 문제 | ^ mutable borrow occurs here 7 | } | - immutable borrow ends here
 

댕글링 참조자(Dangling References)

fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { let s = String::from("hello"); &s }
error[E0106]: missing lifetime specifier --> dangle.rs:5:16 | 5 | fn dangle() -> &String { | ^^^^^^^ | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from = help: consider giving it a 'static lifetime error: aborting due to previous error
fn dangle() -> &String { // dangle은 String의 참조자를 반환합니다 let s = String::from("hello"); // s는 새로운 String입니다 &s // 우리는 String s의 참조자를 반환합니다. } // 여기서 s는 스코프를 벗어나고 버려집니다. 이것의 메모리는 사라집니다. // 위험하군요!
 

참초자의 규칙

다음 중 오직 한 가지만 사용 가능하다
  • 하나의 가변 참조자
  • 임의 갯수의 불편 잠조자