1.4.0으로 업데이트

sarojaba authored
revision b472fa5e843c310705f64b6ef3f049ba1f09fbf3
references-and-borrowing
# 5.98. 참조와 빌림 (References and Borrowing) - 100%

이 가이드는 러스트의 소유권 시스템을 설명하는 세 가이드 중 하나입니다. 이는 러스트의 가장 고유하면서 매력적인 동시에 러스트 개발자들이 반드시 친숙해져야 하는 특징입니다. 소유권을 통해 러스트는 스스로의 가장 거대한 목표인 메모리 안정성을 성취해냅니다. 관련된 몇가지 개념들이 있는데, 각각을 설명하는 장이 존재합니다. 다음과 같죠:

* 핵심 개념인 [소유권][ownership]
* 여러분이 읽고 계시는 참조와 빌림
* [수명][lifetimes], 빌림의 고급 개념

이 세 장들은 연관되어 있고, 순서대로 작성되어 있습니다. 소유권 시스템을 이해하기 위해선 세 장을 모두 필요로 할 것입니다.

[ownership]: ownership.html
[lifetimes]: lifetimes.html

# 개괄

세부사항을 살펴보기 전에, 소유권 시스템에 관한 두 가지 중요한 점을 짚고 넘어가겠습니다.

러스트는 안정성과 속도에 초점을 맞추고 있고, 이러한 목표들을 많은 '무비용 추상화 _zero-cost abstraction_'들을 통해 이루어냅니다. 다시 말해, 러스트에서 추상화는 그것이 작동하기 위해 필요한 최소한의 비용만을 필요로 합니다. 소유권 시스템은 무비용 추상화의 대표적인 예입니다. 이 가이드에서 이야기할 모든 분석들은 _컴파일 타임 내에 _이루어집니다. 이러한 기능들을 사용하기 위해 런타임에서 비용을 치를 필요는 없다는 이야기죠.

하지만, 이 시스템 역시 치뤄야할 비용은 있기 마련인데, 바로 학습 곡선입니다. 러스트를 처음 접하는 많은 사용자들은 작성자는 아무 문제가 없다고 생각하는 프로그램을 러스트 컴파일러가 컴파일하기 거부하는 현상에 맞닥뜨립니다. 우리가 '소유권 검사기와의 싸움'이라고 부르는 현상이죠. 이는 보통 소유권이 어떻게 동작해야 하는 지에 대한 프로그래머의 생각과 실제 러스트가 구현하는 규칙들이 다르기 때문에 발생합니다. 이 글을 읽고 있는 당신도 처음엔 비슷한 경험을 할지 모릅니다. 좋은 소식은, 이러한 소유권 시스템 규칙들과 함께 작업을 하면 할수록 소유권 검사기와 싸울 일은 점점 적어진다고 다수의 숙련된 러스트 개발자들이 말한다는 거죠.

그걸 명심하시고, 빌림에 대해 배워봅시다.

# 빌림

[소유권][ownership] 섹션의 끝자락에서, 다음과 같은 복잡한 예제를 보았습니다:

```rust
fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) {
// do stuff with v1 and v2

// hand back ownership, and the result of our function
(v1, v2, 42)
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];

let (v1, v2, answer) = foo(v1, v2);
```

하지만, 사실 이 코드는 이상적인 러스트 코드는 아닙니다. 빌림 기능을 제대로 활용하지 못 하고 있기 때문이죠. 이상적인 코드로의 첫 단계는 다음과 같습니다:

```rust
fn foo(v1: &Vec, v2: &Vec) -> i32 {
// do stuff with v1 and v2

// return the answer
42
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];

let answer = foo(&v1, &v2);

// we can use v1 and v2 here!
```

위의 함수에서 우리는 `Vec` 타입을 인자로 받는 대신, 그에 대한 참조인 `&Vec`를 받습니다. 그리고 `v1`과 `v2`를 직접 넘기는 대신, `&v1`과 `&v2`를 넘기죠. `&T` 타입은 '참조'라고 불리는데, 이러한 타입은 리소스의 소유권을 갖지 않고 소유권을 빌려옵니다. 이렇듯 '빌려 오는' 바인딩은 그 바인딩의 스코프가 끝날 때 리소스의 할당을 해제하지 않습니다. 다시 말해, `foo()`의 호출이 끝난 후에도 기존의 바인딩을 다시 사용할 수 있는거죠.

바인딩과 마찬가지로, 참조는 불변_immutable_합니다. 즉, `foo()` 내에서 벡터들은 전혀 바뀔 수 없습니다:

```rust,ignore
fn foo(v: &Vec) {
v.push(5);
}

let v = vec![];

foo(&v);
```

는 다음과 같은 에러를 뱉죠:

```text
error: cannot borrow immutable borrowed content `*v` as mutable
v.push(5);
^
```

값을 푸쉬하는 것은 벡터를 변경하는 일이고, 따라서 허용되지 않습니다.

# &mut 참조

두번째 타입의 참조가 있습니다. `&mut T` 인데, 이러한 '가변_mutable_ 참조'는 당신이 빌린 리소스를 변경할 수 있게 합니다. 예를 들어:

```rust
let mut x = 5;
{
let y = &mut x;
*y += 1;
}
println!("{}", x);
```

위의 코드는 `6`을 출력합니다. 우리는 `x`에 대한 가변 참조로서 `y`를 만들었고, `y`가 가리키고 있는 것에 1을 더해줬죠. 이 때, `x` 역시 선언 당시 `mut`로 명시되었어야 합니다. 그렇지 않은 경우, 불변 값으로의 가변 빌림은 생성이 불가능합니다.

You'll also notice we added an asterisk (`*`) in front of `y`, making it `*y`,
this is because `y` is an `&mut` reference. You'll also need to use them for
accessing the contents of a reference as well.

그 점을 제외하면, `&mut` 참조는 기존의 참조와 비슷합니다. 다만, 실제로 그들이 어떻게 상호작용하는지에는 _지대한_ 차이가 존재하죠. 위의 예제에서, `{`와 `}`를 이용해 새로운 스코프를 만들어야 했다는 점에서 뭔가 수상한 점을 찾으셨을지도 모르겠네요. 만약 중괄호들을 제거하면 다음과 같은 에러가 발생합니다:

```text
error: cannot borrow `x` as immutable because it is also borrowed as mutable
println!("{}", x);
^
note: previous borrow of `x` occurs here; the mutable borrow prevents
subsequent moves, borrows, or modification of `x` until the borrow ends
let y = &mut x;
^
note: previous borrow ends here
fn main() {

}
^
```

알고 보니, 규칙들이 있는 모양이네요.

# 규칙들

다음은 러스트에서의 빌림에 관한 규칙들입니다:

첫째, 어떤 빌림이든 원래 주인보다 크지 않은 스코프에서만 생존할 수 있다. 둘째, 어떤 리소스에 대해 다음 두 종류의 빌림 중 어느 쪽이든 가질 수 있지만, 두 종류의 빌림을 동시에 유지할 수는 없다.

* 한 리소스로의 1개 또는 그 이상의 참조(`&T`)
* 한 리소스로의 정확히 1개의 가변 참조(`&mut T`)

어쩌면 이게 데이터 레이스의 정의와 굉장히 비슷한, 하지만 완벽히 같지는 않은 상황이라는 것을 알아차렸을지도 모르겠네요:

>만약 어떤 메모리 주소에 두 개 이상의 포인터가 동시에 접근하고, 그들 중 하나 이상의 포인터가 쓰기 작업을 하면서 명령들이 동기화되지 않는다면, '데이터 레이스'가 발생합니다.

만약 당신이 쓰기(변경) 작업을 하지 않는다면, 한 리소스에 대해 당신이 원하는 만큼의 참조가 가능합니다. 만약 쓰기 작업을 할거라면, 그 시점에서는 딱 하나의 `&mut` 참조만을 가질 수 있습니다. 이게 러스트가 컴파일 타임에 데이터 레이스를 방지하는 비결입니다: 만약 규칙을 어기면, 에러가 나게 되죠.

이 점을 명심한 채로, 예제를 다시 한 번 살펴봅시다.

## 스코프에 대해

다음 코드를 보시죠:

```rust,ignore
let mut x = 5;
let y = &mut x;

*y += 1;

println!("{}", x);
```

이 코드는 다음과 같은 에러를 발생시킵니다:

```text
error: cannot borrow `x` as immutable because it is also borrowed as mutable
println!("{}", x);
^
```

규칙을 어겼기 때문에 일어나는 현상인데, `x`를 가리키는 `&mut T`를 가지고 있기 때문에 다른 `&T`를 생성할 수 없죠. 둘 중 하나만 골라야 하는 것, 기억 하시나요? 에러 메시지의 노트는 이 문제를 해결할 힌트를 줍니다:

```text
note: previous borrow ends here
fn main() {

}
^
```

다시 말해, 가변 참조가 우리의 예제의 나머지 내내 유지된다는거죠. 우리가 원하는 것은 `println!`의 호출 _이전에_ 가변 참조가 긑이 나고, 다른 불변 참조의 생성이 가능해지는 것이죠. 러스트에서, 빌림은 그 빌림이 유효한 스코프와 짝지어집니다. 그리고 현재 스코프는 다음과 같죠:

```rust,ignore
let mut x = 5;

let y = &mut x; // -+ &mut borrow of x starts here
// |
*y += 1; // |
// |
println!("{}", x); // -+ - try to borrow x here
// -+ &mut borrow of x ends here
```

스코프 간의 충돌이 일어납니다: `y`가 스코프 내에 있는 동안 `&x`를 만들 수는 없어요.

그래서 다음과 같이 꺾인 괄호를 추가하면:

```rust
let mut x = 5;

{
let y = &mut x; // -+ &mut borrow starts here
*y += 1; // |
} // -+ ... and ends here

println!("{}", x); // <- try to borrow x here
```

문제는 사라집니다. 가변 빌림은 불변 빌림이 일어나기 전에 스코프에서 사라지죠. 스코프는 빌림이 얼마나 오래 유지될지를 확인하는 핵심 요소입니다.

## 빌림으로 인해 예방되는 문제들

왜 이런 제한적인 규칙들을 따라야 하냐고요? 음, 우리가 이미 이야기했듯이 이러한 규칙들은 데이터 레이스를 예방합니다. 데이터 레이스가 일어나면 어떤 문제들이 일어날까요? 몇 가지 살펴봅시다.

### 반복자 무효화 _Iterator invalidation_

한 예는 '반복자 무효화'입니다. 당신이 반복중인 콜렉션을 변경하려 할 때 일어나죠. 러스트의 소유권 검사기는 이러한 일을 방지합니다:

```rust
let mut v = vec![1, 2, 3];

for i in &v {
println!("{}", i);
}
```

위의 코드는 1에서 3까지를 출력합니다. 벡터를 따라 반복하면서, 우리는 각 원소에 대한 참조만을 갖게 됩니다. `v` 자체가 불변 형식으로 빌려졌기 때문에, 반복자를 도는 동안 그것을 변경할 수 없음을 알 수 있죠:

```rust,ignore
let mut v = vec![1, 2, 3];

for i in &v {
println!("{}", i);
v.push(34);
}
```

위의 코드는 다음과 같은 에러를 발생시킵니다:

```text
error: cannot borrow `v` as mutable because it is also borrowed as immutable
v.push(34);
^
note: previous borrow of `v` occurs here; the immutable borrow prevents
subsequent moves or mutable borrows of `v` until the borrow ends
for i in &v {
^
note: previous borrow ends here
for i in &v {
println!(“{}”, i);
v.push(34);
}
^
```

반복문에 의해 `v`가 빌려졌기 때문에, 반복문 내에서 `v`를 변경할 수 없는거죠.

### 해제 이후의 사용 _use after free_

참조는 그것이 가리키고 있는 리소스보다 오래 살아남을 수 없습니다. 러스트는 당신의 참조들의 스코프를 체크해서 이게 늘 참으로 유지될것을 보장합니다.

만약 러스트가 이것을 체크하지 않는다면, 우리는 더이상 유효하지 않은 참조를 사용할지도 모릅니다. 예를 들어 보죠:

```rust,ignore
let y: &i32;
{
let x = 5;
y = &x;
}

println!("{}", y);
```

위의 코드는 다음과 같은 에러를 발생시킵니다:

```text
error: `x` does not live long enough
y = &x;
^
note: reference must be valid for the block suffix following statement 0 at
2:16...
let y: &i32;
{
let x = 5;
y = &x;
}

note: ...but borrowed value is only valid for the block suffix following
statement 0 at 4:18
let x = 5;
y = &x;
}
```

다시 말해, `y`는 `x`가 존재하는 스코프 내에서만 유효하다는 것이죠. `x`가 사라져버린 후에도 `x`를 참조한다는 것은 이치에 맞지 않습니다. 참조의 실제 수명과 그것이 살아남아야 할 시간간의 불일치로 인해 에러가 발생하고, 따라서 `x`가 '충분히 오래 살아있지 않는다'는 에러가 발생하는 것이죠.

참조가 자신이 가리키는 변수의 선언 _이전에_ 선언 될 때도 같은 문제가 발생합니다. This is because resources within the same scope are freed in the opposite order they were declared:

```rust,ignore
let y: &i32;
let x = 5;
y = &x;

println!("{}", y);
```

다음과 같은 에러가 발생합니다:

```text
error: `x` does not live long enough
y = &x;
^
note: reference must be valid for the block suffix following statement 0 at
2:16...
let y: &i32;
let x = 5;
y = &x;

println!("{}", y);
}

note: ...but borrowed value is only valid for the block suffix following
statement 1 at 3:14
let x = 5;
y = &x;

println!("{}", y);
}
```

In the above example, `y` is declared before `x`, meaning that `y` lives longer than `x`, which is not allowed.