hyunsu blog

홈강좌Rust 입문·중급 47편 중 19편째

Rust 가변 참조 — &mut 는 한 번에 하나만 (E0499·E0502)

목차 · 4
  1. 01&mut 로 빌리면 고칠 수 있다
  2. 02가변 참조는 한 번에 하나다
  3. 03읽는 중에는 고칠 수 없다
  4. 04실습 — 다 쓴 뒤에 빌리면 된다

Rust 에서 빌린 값을 고치려면 &mut 로 빌린다. 규칙은 하나다. 같은 값에 대해 읽기 참조(&)는 여러 개, 가변 참조(&mut)는 딱 하나만 동시에 있을 수 있고, 둘을 섞을 수 없다. 어기면 E0499·E0502 가 난다. 참조를 마지막으로 쓴 뒤라면 새로 빌려도 된다.

01 &mut 로 빌리면 고칠 수 있다

18편 끝의 오류를 고쳐 보자. 세 군데에 mut 가 들어간다.

rust
fn add_suffix(text: &mut String) {
    text.push_str("!");
}

fn main() {
    let mut name = String::from("ferris");
    add_suffix(&mut name);
    add_suffix(&mut name);
    println!("{name}");
}
text
   Compiling suffix v0.1.0 (~/projects/suffix)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
     Running `target/debug/suffix`
ferris!!
  1. 매개변수가 &mut String 이다. 고칠 수 있게 빌린다.
  2. 부를 때 &mut name 으로 빌려준다.
  3. 주인 name 도 let mut 다. 바꿀 수 없는 값을 고치라고 빌려줄 수는 없다(3편).

두 번 불렀으니 느낌표가 두 개 붙었다. 주인은 여전히 main 의 name 이다.

02 가변 참조는 한 번에 하나다

같은 값을 &mut 로 두 번 빌려 보자.

rust
fn main() {
    let mut name = String::from("ferris");
    let r1 = &mut name;
    let r2 = &mut name;
    println!("{r1} {r2}");
}
text
error[E0499]: cannot borrow `name` as mutable more than once at a time
 --> src/main.rs:4:14
  |
3 |     let r1 = &mut name;
  |              --------- first mutable borrow occurs here
4 |     let r2 = &mut name;
  |              ^^^^^^^^^ second mutable borrow occurs here
5 |     println!("{r1} {r2}");
  |                -- first borrow later used here
  1. 무엇 — name 을 한 번에 두 번 이상 가변으로 빌릴 수 없다(more than once at a time).
  2. 어디 — 3번째 줄이 첫 번째 가변 빌림, 4번째 줄이 두 번째다. 5번째 줄의 밑줄은 "첫 번째 빌림을 그 뒤에도 쓰고 있다" 는 뜻이다. 두 빌림이 겹친다.
  3. 고치는 법 — help: 는 없다. 겹치지 않게 하나를 다 쓴 뒤에 다음을 빌린다(아래 실습).

둘이 동시에 고칠 수 있으면 한쪽이 고치는 도중에 다른 쪽이 읽거나 고쳐서 값이 엉망이 될 수 있다. Rust 는 이런 일을 컴파일할 때 막는다.

03 읽는 중에는 고칠 수 없다

읽기 참조와 가변 참조를 섞어도 막힌다.

rust
fn main() {
    let mut name = String::from("ferris");
    let r1 = &name;
    let r2 = &mut name;
    r2.push_str("!");
    println!("{r1}");
}
text
error[E0502]: cannot borrow `name` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:14
  |
3 |     let r1 = &name;
  |              ----- immutable borrow occurs here
4 |     let r2 = &mut name;
  |              ^^^^^^^^^ mutable borrow occurs here
5 |     r2.push_str("!");
6 |     println!("{r1}");
  |                -- immutable borrow later used here

r1 이 읽는 동안(3번째 줄에서 빌려 6번째 줄에서 씀) 4번째 줄에서 고치려고 빌렸다. 누가 책을 읽는 사이에 다른 사람이 책장을 찢으면 안 되는 것과 같다.

같은 값을 동시에되나
& 여러 개된다 (18편)
&mut 하나된다
&mut 둘 이상안 된다 (E0499)
& 와 &mut 함께안 된다 (E0502)

04 실습 — 다 쓴 뒤에 빌리면 된다

"동시에" 의 기준은 참조를 마지막으로 쓴 곳까지다. 거기까지만 겹치지 않으면 된다.

rust
fn main() {
    let mut name = String::from("ferris");

    let r1 = &name;
    println!("{r1}");

    let r2 = &mut name;
    r2.push_str("!");
    println!("{r2}");
}
text
   Compiling rules v0.1.0 (~/projects/rules)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
     Running `target/debug/rules`
ferris
ferris!

r1 은 5번째 줄에서 마지막으로 쓰였다. 그 뒤인 7번째 줄에서 &mut 로 빌리니 겹치지 않아 컴파일된다. shared-mut-error 와 비교하면 println!("{r1}") 의 자리만 다르다. 그 줄을 맨 아래로 옮기면 다시 E0502 가 난다.

참조 규칙은 이것으로 끝이다. 다음 편에서는 지금까지 써 온 두 문자열 String 과 &str 이 어떻게 다른지 정리한다.