blog

러스트 참조 및 차용

Rust는 값을 소유하지 않고도 값을 사용할 수 있는 참조라는 기능을 제공합니다. 참조는 해당 주소의 다른 변수에 저장된 데이터에 액세스할 수 있는 주소이기 때문에 포인터와 비슷합...

Oct 31, 2025 · 6 min. read
シェア

인용 및 차용

fn main() {
 let s1 = String::from("hello");
 //  
 let (s2, len) = calculate_length(s1);
 println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
 let length = s.len(); // len() 문자열의 길이를 반환합니다.
 (s, length)
}

위 코드에서는 계산 길이 함수가 호출된 후에도 문자열을 계속 사용할 수 있도록 호출 함수에 문자열을 반환해야 하는데, 이는 문자열이 계산 길이로 이동했기 때문에 불편할 수밖에 없습니다.

참조

Rust는 참조 ()라고 하는 소유권을 가지지 않고 값을 사용할 수 있는 기능을 제공합니다. 참조는 해당 주소에 저장된 다른 변수의 데이터에 액세스할 수 있는 주소라는 점에서 포인터와 같으며, 참조는 특정 유형의 유효한 값을 가리키도록 보장됩니다.

fn main() {
 let s1 = String::from("hello");
 let slen = calculate_string(&s1);
 println!("The length of '{}' is {}.", s1, slen);
}
fn calculate_string(s: &String) -> usize {
 s.len()
}

위의 코드는 참조를 사용하고 있습니다.

문자열 s1을 가리키는 &String s의 메모리 도식입니다:

 let s1 = String::from("hello");
 let slen = calculate_string(&s1);

s1은 s1에 대한 참조를 생성하며, 참조이기 때문에 소유되지 않습니다. 소유되지 않기 때문에 참조가 더 이상 사용되지 않을 때 참조가 가리키는 값은 버려지지 않습니다.

fn calculate_string(s: &String) -> usize {
 s.len()
}

마찬가지로 함수 시그니처에 &String을 사용하면 함수 인자 s가 참조임을 나타냅니다. 참조가 가리키는 데이터는 s를 소유하지 않기 때문에 s가 중단되어도 삭제되지 않습니다. 함수가 실제 값 대신 참조를 인수로 사용하는 경우, 소유권이 없는 값이므로 소유권을 반환하기 위해 값을 반환할 필요가 없습니다.

다른 용도로 빌리다.

참조를 만드는 행위를 차용이라고 합니다.

fn main() {
 let s1 = String::from("hello");
 change(&s1);
}
fn change(s: &String) {
 s.push_str("{}, World!");
}

위의 예에서 빌린 값을 변경하려는 시도가 컴파일 후 작동하지 않는 것으로 나타났습니다.

 |
105 | s.push_str("{}, World!");
 | ^ `s` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 |
help: consider changing this to be a mutable reference
 |
104 | fn change(s: &mut String) {
 | +++

변수가 불변인 것처럼 참조도 불변이며, 참조 값의 수정은 기본적으로 허용되지 않습니다.

변경 가능한 참조

기본적으로 참조 값을 변경할 수 없으므로 참조 값을 변경할 수 있는 방법이 있습니다.

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

변수 s1을 변경 가능하게 만든 다음 변경 함수가 호출되는 &mut s1에 대한 변경 가능한 참조를 생성하고, 변경 함수가 s: &mut 문자열에 대한 변경 가능한 참조를 받아들일 수 있다는 사실을 업데이트하면 변경 함수가 차용하는 값이 변경된다는 것을 나타냅니다.

차용한 값을 수정할 수 있도록 허용하는 것을 변경 가능한 참조라고 합니다.

변수 참조에는 제한이 있습니다. 해당 변수에 대한 변수 참조가 있는 경우 해당 변수에 대한 다른 참조를 만들 수 없습니다.

let mut s = String::from("Hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
 |
101 | let r1 = &mut s;
 | ------ first mutable borrow occurs here
102 | let r2 = &mut s;
 | ^^^^^^ second mutable borrow occurs here
103 |
104 | println!("{}, {}", r1, r2);
 | -- first borrow later used here

이 오류는 여러 변수 차용을 동시에 생성할 수 없음을 나타냅니다. 첫 번째 변수 차용은 r1에 있으며, 이는 println까지 지속됩니다! 그러나 r1 변수 참조를 생성하고 사용하는 사이에 다른 변수 참조가 생성되었습니다.

동일한 데이터에 대한 여러 변경 가능한 참조를 동시에 방지하면 컴파일 시 데이터 경합을 피할 수 있다는 장점이 있습니다.

데이터 경쟁은 세 가지 행동으로 인해 발생할 수 있습니다:

  • 동시에 동일한 데이터에 액세스하는 둘 이상의 포인터
  • 데이터를 쓰는 데 하나 이상의 포인터가 사용됩니다.
  • 데이터 액세스를 동기화하는 메커니즘 없음

데이터 경합은 런타임에 추적하기 어려운 정의되지 않은 동작으로 이어질 수 있는데, Rust는 데이터 경합 없이 코드를 컴파일하기 때문에 이를 방지합니다!

중괄호를 사용하여 변경 가능한 참조를 여러 개 가질 수 있도록 새 범위를 만들있지만 "동시에"는 허용하지 않습니다:

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; // 변경 가능한 참조
println!("{}, {}, {}", r1, r2, r3);

오류는 다음과 같습니다:

 |
101 | let r1 = &s; // 변경 불가능한 참조
 | -- immutable borrow occurs here
102 | let r2 = &s; // 변경 불가능한 참조
103 | let r3 = &mut s; // 변경 가능한 참조
 | ^^^^^^ mutable borrow occurs here
104 | 
105 | println!("{}, {}, {}", r1, r2, r3);
 | -- immutable borrow later used here

변경 가능한 참조를 가지면서 불변 참조를 가질 수도 없습니다! 여러 개의 불변 참조는 괜찮습니다!

let mut s = String::from("hello");
let r1 = &s; // 변경 불가능한 참조
let r2 = &s; // 변경 불가능한 참조
println!("{} and {}", r1, r2);
// 이 위치 이후에는 r1과 r2가 더 이상 사용되지 않습니다.
let r3 = &mut s; // 변경 가능한 참조
println!("{}", r3);

참조의 범위는 선언된 시점부터 마지막으로 사용될 때까지 지속됩니다. 따라서 위의 코드는 컴파일할 수 있습니다.

댕글링 참조

포인터를 사용하는 언어에서는 메모리를 해제할 때 포인터를 유지하여 매달린 포인터를 잘못 생성하기 쉽습니다.

댕글링 포인터는 이미 다른 소유자에게 할당되었을 수 있는 메모리를 가리키는 참조입니다. Rust에서 컴파일러는 참조에 댕글링 포인터가 발생하지 않도록 보장합니다.

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

다음 오류가 발생합니다:

 |
101 | fn dangling_pointer() -> &String {
 | ^ expected named lifetime parameter
 |
 = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
 |
101 | fn dangling_pointer() -> &'static String {

이 오류는 dangling_pointer 함수가 찾을 수 없는 변경 불가능한 참조를 반환했음을 나타냅니다.

왜 그럴까요?

fn main() {
 let value = dangling_pointer();
}
fn dangling_pointer() -> &String {
 let s = String::from("hello"); // 댕글링된 변수_pointer 메서드를 생성하는 메서드는
 &s // 문자열 s에 대한 변경 불가능한 참조를 반환합니다.
} // 여기서 범위가 끝나면 변수 s가 삭제되고 해당 메모리가 해제됩니다.

변수 s는 dangling_pointer 메서드에서 생성되었으므로 해당 메서드의 실행이 완료되면 변수 s도 해제됩니다. 그러면 잘못된 문자열을 가리키는 참조가 반환됩니다!

해결책은 그냥 문자열을 반환하는 것입니다.

fn dangling_pointer() -> String {
 let s = String::from("hello");
 
 s
}

인용문에 대한 요약은 다음과 같습니다:

  • 가변 참조와 불변 참조는 동시에 존재할 수 없으며, 동일한 데이터에 대한 가변 참조를 여러 개 만들 수 없다는 점에 유의하세요.
  • 참조는 항상 유효하며, 유효하지 않은 참조는 매달린 참조를 유발합니다.
Read next

Goooood 컨버지드 CDN으로 콘텐츠 전송 최적화: 종합 솔루션

Goooood 컨버지드 CDN으로 콘텐츠 전송 최적화: 종합 솔루션 오늘날의 디지털 시대에 기업은 온라인 콘텐츠 전송을 최적화하여 사용자 경험을 향상하고 경쟁 환경에서 앞서 나갈 수 있는 방법을 끊임없이 모색하고 있습니다.

Oct 31, 2025 · 6 min read