Rust 초급 5편: 참조와 빌림으로 문자열 읽고 수정하기
4편에서는 문자열을 함수에 보내고 반환받았습니다. 읽기만 하는 함수마다 값을 돌려받으면 코드가 번거로워집니다. 이번에는 함수가 문자열을 잠시 빌려 쓰도록 바꿉니다. 소유권은 main에 남겨 둡니다.

4편에서는 문자열을 함수에 보내고 반환받았습니다. 읽기만 하는 함수마다 값을 돌려받으면 코드가 번거로워집니다. 이번에는 함수가 문자열을 잠시 빌려 쓰도록 바꿉니다. 소유권은 main에 남겨 둡니다.
준비와 목표
새 프로젝트에서 아래 예제를 src/main.rs에 저장하세요. &str로 읽기, &mut String으로 수정을 구분하는 것이 목표입니다.
cargo new study_borrowing
cd study_borrowing핵심 실습: 제목 검사와 접미사 추가
fn character_count(title: &str) -> usize {
title.chars().count()
}
fn append_done(title: &mut String) {
title.push_str(" 완료");
}
fn main() {
let mut title = String::from("Rust 공부");
let count = character_count(&title);
println!("수정 전: {title} ({count}자)");
append_done(&mut title);
println!("수정 후: {title}");
}cargo run수정 전: Rust 공부 (7자)
수정 후: Rust 공부 완료&title은 값을 읽을 수 있는 참조를 만듭니다. 함수는 그 참조를 통해 문자열을 보지만 String의 소유자가 되지 않습니다. 따라서 함수 호출이 끝난 뒤 main에서 title을 그대로 사용할 수 있습니다.
character_count의 매개변수는 &String 대신 &str입니다. 이렇게 하면 String에서 빌린 문자열과 문자열 리터럴을 모두 받기 편합니다. String 참조는 이 호출에서 &str로 변환되어 전달됩니다. 문자열 슬라이스 공식 설명
chars().count()는 Unicode 스칼라 값의 개수를 셉니다. 이번 예제의 단순한 한글과 영문에서는 화면에서 예상한 개수와 같지만, 결합 문자나 여러 코드 포인트로 이뤄진 이모지의 시각적 글자 수까지 보장하지는 않습니다. String의 len()은 UTF-8 바이트 수이므로 이 둘을 혼동하지 마세요.
수정하려면 두 군데에서 허용하기
append_done은 &mut String을 받습니다. 가변 참조이므로 원래 문자열을 수정할 수 있습니다. 이를 전달하는 쪽에서도 title을 let mut로 선언하고 &mut title로 빌려줘야 합니다.
빌린다고 해서 함수가 무엇이든 할 수 있는 것은 아닙니다. &str은 읽기, &mut String은 이 예제에서 수정 권한을 드러냅니다. 함수의 인자 타입만 봐도 원본을 바꿀 가능성을 알 수 있습니다.
동시에 빌릴 때의 규칙
같은 데이터에 대해 가변 참조가 실제로 사용되는 동안 다른 참조를 겹쳐 사용하는 데 제한이 있습니다. 읽기 참조는 여러 개 만들 수 있지만, 수정 가능한 참조와 충돌하도록 쓰면 컴파일러가 거부합니다. 참조와 빌림 공식 설명
아래는 의도적으로 실패하는 전체 예제입니다.
fn main() {
let mut title = String::from("Rust 공부");
let before = &title;
let editor = &mut title;
editor.push_str(" 완료");
println!("{before}");
}cargo check를 실행하면 읽기 참조 before가 뒤에서 다시 쓰이는데 가변 참조로 빌렸다는 오류가 나옵니다. before를 만든 줄뿐 아니라 마지막 println!까지 확인해야 하는 이유입니다.
겹치지 않게 순서 정하기
위 예제를 다음으로 바꾸면 됩니다.
fn main() {
let mut title = String::from("Rust 공부");
let before = &title;
println!("먼저 읽기: {before}");
let editor = &mut title;
editor.push_str(" 완료");
println!("이후 수정: {title}");
}먼저 읽기: Rust 공부
이후 수정: Rust 공부 완료before를 마지막으로 쓰는 지점이 수정 이전으로 옮겨졌습니다. 참조가 반드시 중괄호 끝까지 충돌한다고 외우기보다 마지막 사용 지점을 확인하세요. 필요하다면 중괄호로 읽기 구간을 따로 묶어 의도를 드러낼 수도 있습니다.
자주 만나는 실수
원본 title의 mut만 지우면 가변으로 빌릴 수 없습니다. append_done(&title)처럼 호출하면 함수가 요구하는 &mut String과 맞지 않습니다. 반대로 읽기 전용 함수까지 모두 &mut로 만들면 불필요한 제약이 늘어납니다.
함수 안에서 만든 String을 가리키는 참조를 밖으로 반환하려는 시도도 안 됩니다. 함수가 끝나며 사라지는 값의 주소를 돌려줄 수 없기 때문입니다. 새 값을 만들어 반환하려면 String 자체를 반환하는 방법을 먼저 떠올리세요.
연습과 해설
appenddone을 appendnote로 바꾸고, 두 번째 인자로 note: &str을 받아 보세요. 함수 안에서는 title.pushstr(note);를 실행하고 main에서 appendnote(&mut title, " 복습");를 호출합니다.
해설: 함수 선언은 fn append_note(title: &mut String, note: &str)입니다. 수정할 대상과 읽기만 할 문자열의 권한이 다릅니다. 이 구분을 함수 타입에 적는 것이 핵심이며 출력은 "Rust 공부 복습"이 됩니다.
다음 6편에서는 제목과 완료 여부를 하나의 구조체로 묶고, 지금 배운 참조를 메서드의 self에 적용합니다.
실행 확인
이 글의 핵심 실습은 Rust 1.99.1, 2024 Edition의 Linux 환경에서 확인했습니다. 의도적으로 실패하는 예제는 설명한 오류가 발생하는지 구분해 검사했습니다. 운영체제별 오류 문구는 다를 수 있습니다.



Discussion
No comments yet