일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- rust
- AWS
- Custom Resource
- prometheus
- 러스트
- 단축 평가
- api gateway
- Kubernetes
- elasticsearch
- 온프레미스
- 옵셔널 체이닝
- transit gateway
- null 병합 연산자
- cognito
- optional chaining
- 구조분해 할당
- JavaScript
- Site-to-Site VPN
- grafana
- docker
- On-Premise
- Service
- Await
- vgw
- 자바스크립트
- Proxy Resource
- 비구조화 할당
- Endpoints
- VPC
- docker swarm
- Today
- Total
만자의 개발일지
[Rust] 참조와 대여 본문
지난 포스팅에서는 소유권과 메모리 모델에 대해 알아보았습니다. 이번 포스팅에서는 소유권의 이동 없이 데이터를 다루는 방식인 참조와 대여에 대해 알아보도록 하겠습니다.
참조(References)와 대여(Borrowing)
Rust에서 참조는 데이터의 메모리 주소를 가르키는 포인터와 유사합니다. 참조를 통해 실제 데이터를 가지는 것이 아닌 해당 데이터의 메모리 주소를 가르킴으로써 소유권을 가져오지 않고 해당 데이터에 접근할 수 있습니다.
다음 예시를 보겠습니다.
fn main() {
let s1: String = String::from("hello");
let s: &String = &s1;
}
s는 참조자(&)를 사용해 s1의 값을 참조하였고 String이 아닌 &String 타입을 사용하였습니다. 이는 참조자가 하나의 타입임을 나타냅니다. s는 실제 데이터를 소유하고 있는 것이 아닌 아래 이미지와 같이 s1의 주소를 가르키고 있습니다.
참조에는 불변 참조와 가변 참조 두 가지 방식이 있습니다.
불변 참조(immutable references)
불변 참조는 데이터에 읽기 권한만 부여합니다. 즉, 불변 참조를 통해 데이터를 수정할 수 없습니다.
불변 참조는 &T 형태로 사용합니다. 다음 예시를 보겠습니다.
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("r1: {}, r2: {}",r1 ,r2);
}
r1과 r2 모두 s의 불변 참조입니다. 한 데이터에 대해 여러 개의 불변 참조를 가질 수 있습니다. r1과 r2 모두 s의 값인 hello를 출력합니다.
다음 예시를 보겠습니다.
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
// r1.push_str(" world"); // `r1` is a `&` reference, so the data it refers to cannot be borrowed as mutable
println!("r1: {}, r2: {}",r1 ,r2);
}
s에 대해 불변 참조를 활성화한 상태에서 s를 변경하려고 했지만 에러가 발생했습니다. 이는 Rust가 데이터의 일관성을 보장하기 위해 불변 참조가 활성화되있는 동안 원본 데이터를 변경할 수 없도록 제한하였기 때문입니다.
가변 참조(mutable references)
위 에러 메세지를 살펴보면 r1이 가변으로 대여되어있지 않기 때문이라고 나와있습니다. 참조자 역시 가변으로 생성할 수 있는데, 이를 가변 참조라고 합니다.
가변 참조는 데이터에 읽기, 수정 권한 모두 부여합니다. 가변 참조는 &mut T 형태로 사용하며, 불변 참조와 달리 오직 하나의 가변 참조만 존재할 수 있습니다.
다음 예시를 보겠습니다.
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // error[E0499]: cannot borrow `s` as mutable more than once at a time
r1.push_str(" world");
println!("r1: {}", r1);
println!("s: {}", s);
}
r1은 s를 가변 참조하였고, r1을 통해 원본 데이터를 수정하였습니다. 하지만 똑같이 s를 가변 참조하는 r2를 선언하니 에러가 발생했습니다. 가변 참조가 활성화되어 있는 동안에는 다른 가변 참조를 생성할 수 없습니다. Rust는 한 데이터가 동시에 수정되는 것을 방지하기 위해 가변 참조가 2개이상 생성하는 것을 허용하지 않습니다.
단, 다음과 같이 기존의 가변 참조 사용이 종료되면 다시 가변 참조를 생성할 수 있습니다.
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
r1.push_str(" world");
println!("r1: {}", r1);
println!("s: {}", s);
let r2 = &mut s;
r2.push_str("!!");
println!("r2: {}", r2);
}
컴파일러는 기존의 가변 참조 변수가 마지막으로 사용되는 시점에서 해당 변수가 더 이상 사용되지 않는다고 판단하고 원본 데이터에 대한 배타적 잠금(exlusive lock)을 해제합니다. 이를 통해 새로운 가변 참조 변수의 생성을 허용합니다.
대여 검사기(borrow checker)
참조자를 통해 소유권을 가져오지 않으면서 해당 값에 접근하는 행위를 대여라고 합니다. 대여에는 다음과 같은 규칙들이 있습니다.
- 참조 값은 다음 중 하나만 가질 수 있습니다. (가변 참조와 불변 참조가 동시에 존재할 수 없음)
- 하나 이상의 불변 참조(&T)
- 하나의 가변 참조(&mut T)
- 참조는 항상 유요해야 합니다. 참조가 가르키는 원본 데이터가 참조보다 먼저 해제되면 안됩니다.
대여 검사기는 컴파일러의 구성 요소 중 하나로, 위에서 설명한 대여 규칙을 따르는지 컴파일 타임에 검사하는 도구입니다. 개발자가 다음과 같이 대여 규칙을 위반하는 코드를 작성하면 오류 메세지를 출력하고 컴파일을 거부합니다.
1. 불변 참조와 가변 참조를 동시에 사용하는 경우
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;
// error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
println!("{}", r1);
r2.push_str(" world");
println!("{}", r1);
}
2. 두개 이상의 가변 참조를 사용하는 경우
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
// error[E0499]: cannot borrow `s` as mutable more than once at a time
r1.push_str(" world");
r2.push_str("!!");
println!("{}", s);
}
대여 검사기는 후에 다룰 라이프타임이라는 개념을 사용하여 위 규칙들을 검사합니다.
댕글링 참조(Dangling References)
댕글링 참조는 위에서 설명한 대여 규칙 중 하나인 이미 해제된 메모리를 참조하는 경우를 의미합니다. 어떤 변수에 메모리를 할당받아 사용하다가, 해당 메모리가 해제되었음에도 그 변수를 사용한다면 예측 불가능한 동작을 초래할 수 있습니다.
다음 예시를 보겠습니다.
fn main() {
let r = {
let s = String::from("hello");
&s
// ^^ borrowed value does not live long enough
}; // `s` dropped here while still borrowed
println!("{}", r);
}
s는 블록이 종료됨과 동시에 drop 함수가 호출되어 메모리에서 해제됩니다. 때문에 s의 참조는 더 이상 유효하지 않게되고 댕글링 참조가 발생하게됩니다.
이처럼 대여 검사기는 후에 다룰 라이프타임(lifetimes)이라는 개념을 통해 대여 규칙을 확인하고 댕글링 참조가 생성되는 것을 방지합니다.
역참조(Dereferences)
역참조(*)는 참조가 가르키는 메모리 주소를 통해 실제 값에 접근하는 것을 의미합니다.
다음 예시를 보겠습니다.
fn main() {
let mut x = 5;
let r = &mut x;
*r += 5;
println!("{}", x);
}
r은 x를 가변 참조하고 있습니다. 이때 r을 역참조하여 r이 가르키는 실제 값인 x에 접근하여 값을 변경할 수 있습니다.
역참조 연산자는 해당 타입이 Copy 트레이트를 구현한 경우 값을 복사하지만, 그렇지 않은 경우에는 이동(Move)을 시도합니다. 따라서 문자열과 같이 Copy 트레이트가 구현되어 있지 않은 타입에 대해 역참조를 시도하는 경우 다음과 같이 오류가 발생합니다.
fn main() {
let s = String::from("hello");
let r = &s;
let s2 = *r; // error[E0507]: cannot move out of `*r` which is behind a shared reference
}
역참조를 통해 원본 데이터의 소유권을 가져오게되면 댕글링 참조가 발생할 수 있기 때문에 데이터에 대한 참조가 존재하는 동안에는 이동에 대한 역참조는 사용할 수 없습니다.
대신 clone 메소드를 사용해 명시적으로 값을 복사할 수 있습니다.
fn main() {
let s = String::from("hello");
let r = &s;
let s2 = r.clone();
}
또한 Deref 트레이트를 구현하여 역참조 연산자 동작을 커스터마이징 할 수도 있습니다. 이는 후에 다루도록 하겠습니다.
ref
패턴 매칭에서 매칭된 값은 기본적으로 패턴 변수로 소유권이 이동하는데, ref 키워드를 사용하면 소유권을 이동시키지 않고 참조할 수 있습니다.
다음 예시를 보겠습니다.
fn main() {
let value = Some(String::from("Hello, Rust"));
match value {
Some(s) => println!("Got: {}", s),
None => println!("Got None"),
}
println!("Value: {:?}", value); // error[E0382]: borrow of partially moved value: `value`
}
value가 가지는 값은 match 블록 내부에 패턴 변수 s로 소유권이 이동하여 값을 가지고 있지 않은 상태에서 value에 접근하려하여 에러가 발생했습니다.
이 경우 ref를 사용하면 해결할 수 있습니다.
fn main() {
let value = Some(String::from("Hello, Rust"));
match value {
Some(ref s) => println!("Got: {}", s),
None => println!("Got None"),
}
println!("Value: {:?}", value);
}
또한 패턴 매칭에서 가변 참조를 얻고 싶을 때는 ref mut을 사용하여 가변 참조를 얻을 수 있습니다.
fn main() {
let mut value = Some(String::from("Hello, Rust"));
match value {
Some(ref mut s) => {
println!("Got: {}", s);
s.push_str("!!");
},
None => println!("Got None"),
}
println!("Value: {:?}", value);
}
이번 포스팅에서는 참조와 대여에 대해 알아보았습니다. 다음 포스팅에서는 제네릭에 대해 알아보도록 하겠습니다.
참고
'Rust' 카테고리의 다른 글
[Rust] 제네릭 (4) | 2025.07.29 |
---|---|
[Rust] 소유권과 메모리 모델 (3) | 2025.06.17 |
[Rust] 패턴 매칭 (0) | 2025.05.31 |
[Rust] 열거형 (0) | 2025.05.18 |
[Rust] 구조체와 메서드 & 연관 함수 (0) | 2025.05.04 |