RustBook - 4.2 References and Borrowing

숲사람·2022년 3월 23일
0

Rust

목록 보기
8/15
post-custom-banner

이 시리즈는 Rust Book을 공부하고 정리한 문서입니다. 댓글로 많은 조언 부탁드립니다.
Rust Book: https://doc.rust-lang.org/book/


4.2 References and Borrowing

References 란

address를 통해 값을 접근할 수 있는 방법.
값을 참고만 하되 오너십을 갖지는 않는 방법 : & 사용

&String s pointing at String s1

Borrowing

함수 파라미터에 오너십을 넘기지 않고 값만 넘기는 것. (We call having references as function parameters borrowing.)

  • 함수 인자로 s1 대신에 &s1
  • 함수 정의에서는 매개변수 부분을 (변수명: &타입) 으로 정의
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()
}

s1은 calculate_length() 로 넘겼음에도 불구하고 해당 함수가 종료되어도 drop 되지 않는다.

fn main() {
    let s = String::from("hello");
    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}

cannot borrow immutable borrowed content *some_string as mutable

위의 코드 처럼 borrow 된 값은 변경이 안된다. Mutable reference로 선언해야한다.

Mutable References

String을 mut으로 선언해야하고 전달할때도 &mut 으로 전달 해야한다.

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

한 변수의 mutable reference는 동일 스코프에 딱 하나만 가질 수 있다. 아래 같은 코드는 컴파일 에러 발생. 이는 Data Race를 방지하기 위해서이다.

let  mut s = String::from("hello"); 
let r1 = &mut s; 
let r2 = &mut s;

Data Race는 다음과 같은 조건에서 발생할 수 있다. Rust는 여러가지 제약을 통해 이를 컴파일 타임에 방지한다.

  • 두개 이상의 포인터가 동시에 한 데이터를 접근
  • 그 둘중하나가 데이터를 write 하도록 사용될때
  • 그런 상황에 동시성 메커니즘이 없을때

다음과같이 한 스코프 당 하나의 &mut s 가 있는경우는 문제 없다.

let mut s = String::from("hello");
{
    let r1 = &mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.
let r2 = &mut s;

하지만 같은 스코프에 mutable 과 immutable reference 를 동시에 사용하는 경우는 에러가 발생한다. 반면 immutable reference 를 여러개 사용하는 것은 문제가 없는데 이는 데이터를 변경하는것이 애초에 불가능 하기 때문이다.

let  mut s = String::from("hello"); 
let r1 = &s; // no problem  
let r2 = &s; // no problem  
let r3 = &mut s; // BIG PROBLEM
  • mutable reference로 오리지널값을 수정하는경우
let mut s = String::from("hello");

let r1 = &mut s;
r1.push_str(", world");
println!("{}", s);

hello world 출력
이런 경우 에러 안남. s 출력 됨.

  • 아래 에러는 솔직히 이해 못함.
    가변, 불변 참조자를 혼용해서 써서인듯. mutable reference가 한 스코프에 두개가 있어서.
fn main() {
    let mut s = String::from("hello");
    
    let mut r1 = &mut s;
    let r2 = &mut r1; 
    r2.push_str(", world2");
    println!("{} ", r1);
    println!("{} ", r2);
    println!("{} ", s); 
}
error[E0502]: cannot borrow `r1` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:21
  |
5 |     let r2 = &mut r1;
  |              ------- mutable borrow occurs here
6 |     r2.push_str(", world2");
7 |     println!("{} ", r1);
  |                     ^^ immutable borrow occurs here
8 |     println!("{} ", r2);
  |                     -- mutable borrow later used here

error: aborting due to previous error

For more information about this error, try rustc --explain E0502.
error: Could not compile references.


print 순서를 아래와 같이 바꾸면 컴파일 성공한다. 
각 변수에 대해서 아래 `|` 으로 표기한 범위 처럼 스코프가 생성되는듯 하다. (?)

```rust
fn main() {
 | let mut s = String::from("hello");
 |   
 | | let mut r1 = &mut s;
 | | | let r2 = &mut r1; 
 | | | r2.push_str(", world2");
 | | | println!("{} ", r2);
 | | println!("{} ", r1);
 |  println!("{} ", s); 
}

Dangling References

메모리가 해제된 포인터를 댕글링 포인터라고 한다. 마찬가지로 레퍼런스도 댕글링 레퍼런스가 있다. 스코프를 벗어나 무효화(Invalid)된 변수의 레퍼런스를 리턴하는것이다. 레퍼런스의 변수는 항상 Valid 해야한다. Rust는 이런 댕글링 레퍼런스를 컴파일타임에 찾아준다.

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

에러 메시지는 다음과 같다. (lifetime 10장 참고)
error[E0106]: missing lifetime specifier

이럴때는 &s가 아닌 s 를 반환 하는게 좋다.

Reference 규칙

  1. 아래 중 둘 중하나만 가질 수 있음
    • 하나의 mutable reference
    • 여러개의 immutable reference
  2. reference는 항상 valid 해야한다.
profile
기록 & 정리 아카이브용
post-custom-banner

0개의 댓글