Rust의 표준 라이브러리에 있는 다양한 트레이트들 중에서 러스트를 사용함에 있어 큰 영향을 미치는 것들을 가리킨다고 한다. 러스트답다
고 평가받을 만한 코드나 인터페이스를 설계하기 위해서는 꼭 알아야 할 것 같아서 정리하게 되었다.
Drop
, Deref
, DerefMut
, From
, Into
,Sized
, Copy
등등에 대해서 공부하고 알아보자
기본적으로 Rust에서는 값의 소유자가 사라지면 그 값을 드롭(drop)한다. 이때 그 값이 소유한 다른 값들과 Heap 등 관련된 자원과 메모리는 드롭 과정에서 전부 해제된다. 드롭은 변수가 범위를 벗어날 때, 표현식의 실행문이 끝날때 등등 다양한 상황에서 일어난다.
대부분의 경우에는 Rust에서 값을 알아서 드롭해 준다.
struct Marvel {
name: String,
nickname: Vec<String>
}
Marvel
은 이름을 저장하는 문자열과 닉네임의 요소를 저장하기 위한 벡터 힙을 가지고 있다. 기본적으로 사용자가 따로 정의 하지 않아도 Marvel
이 사용되고 드롭될 때마다 알아서 정리해준다. 그렇지만 원한다면 std::ops::Drop
트레이트를 구현해서 Rust의 드롭 과정에 관여할 수 있다.
trait Drop {
fn drop(&mut self);
}
기본적인 형태는 위와 같다.
만일 드롭되는 값이 std::ops::Drop
을 구현하고 있을 경우, 전부 드롭하기 전에 drop
메소드를 호출한다. 이는 Rust만이 직접 호출할 수 있다.
이 호출 시점은 해당 값의 필드나 요소가 아직 드롭되기 전이므로 메소드로 전달되는 값은 항상 값이 초기화된 상태로 넘어오게 된다.
impl Drop for Marvel {
fn drop(&mut self) {
println!("드롭!! {}", self.name)
if !self.nickname.is_empty() {
println!(" (AKA {})", self.nickname.join(", "))
}
println!("");
}
}
// main.rs
fn main() {
let mut a = Marvel {
name: "아이언맨".to_string(),
nickname: vec!["3000만큼".to_string(), "따뜻한 강철 심장".to_string()]
};
println!("시작");
a = Marvel {
name: "토르".to_string(),
nickname: vec!["천둥의 신".to_string(), "묠니르".to_string()]
};
println!("종료");
}
두번 째 a
에 값을 새로 배정하면 첫번 째 값이 드롭되고 a가 실행을 완료해서 범위를 벗어나게 되면 두번 째 값이 드롭된다.
Vec
은 Drop
을 구현하고 있다
fn drop(&mut self)
이를 통해서 자신이 가진 요소들을 드롭하고, 힙 할당 메모리를 해제한다. String
은 내부적으로 Vec<u8>
을 써서 자신의 텍스트를 보관하기 때문에 Drop
을 따로 구현하고 있지 않으며 문자들을 해제하는 일은 Vec
에게 위임하여 처리한다.
GC(Garbage Collector)가 없는 Rust는 개발자가 직접 메모리 할당과 해제를 제어할 수 있는 기능을 제공한다. Rust만의 소유권
, 참조 관리(빌림 규칙)
등을 통해 메모리 안정성을 보장하고 개발자는 객체의 소유권을 명확하게 지정하여 해당 객체가 사용되지 않을 때 자동으로 컴파일러가 해체할 수 있게 해야한다.