프로그래밍백엔드 개발자

러스트의 소유권 시스템이 어떻게 작동하며 어떻게 가비지 컬렉터 없이 메모리 안전성을 보장하는지 설명해 주세요?

Hintsage AI 어시스턴트로 면접 통과

답변

소유권 시스템(ownership)은 러스트의 메모리 안전성을 컴파일 수준에서 보장하는 기본 개념입니다. 각 변수는 소유자를 가지고 있습니다. 특정 시점에 자원의 소유자는 오직 하나만 존재할 수 있습니다.

소유권은 할당하거나 함수에 전달할 때 전송(move)됩니다. 이전 소유자가 값을 사용할 수 없게 됩니다:

let s = String::from("hello"); let t = s; // s는 더 이상 사용할 수 없음

러스트는 빌림(borrow)도 구별합니다.

  • &T — 읽기 전용 참조.
  • &mut T — 가변 참조, 그러나 동시에 하나만 존재할 수 있습니다.

이 규칙들 덕분에 러스트는 메모리가 해제된 후에 사용되는 데이터 레이스와 기타 자원 관리 오류가 발생하지 않도록 보장합니다.

트릭 질문

하나의 값에 대해 동일하게 여러 개의 가변 참조(&mut T)가 동시에 있을 수 있나요? 왜요?

답: 아닙니다, 어느 순간에 불변 참조는 무수히 많을 수 있지만, 가변 참조는 오직 하나만 존재할 수 있습니다. 이는 데이터 레이스를 방지합니다. 잘못된 코드 예:

let mut s = String::from("hi"); let r1 = &mut s; let r2 = &mut s; // 컴파일 오류!

주제의 미세한 차이를 모르는 것으로 인한 실제 오류의 예


이야기

하나의 멀티스레드 프로젝트에서 개발자는 소유권을 사용하지 않고 가변 버퍼에 대한 참조를 저장하려고 하였고, 이로 인해 버퍼가 해제된 후에 사용되는 오류(use-after-free)가 발생했습니다. 러스트는 이러한 코드를 컴파일할 수 없도록 하였고, 결국 아키텍처를 변경해야 했습니다.


이야기

큰 러스트 프로젝트의 시작에서 프로그래머들은 C++에서 코드를 아카이브하였습니다. 그들은 구식의 "원시" 포인터 작업 방식을 유지하려 했고, 이는 자원 소유와 관련한 오류와 borrow checker의 지속적인 패닉을 초래했습니다. 결국 소유권 아키텍처 문제를 해결해야 했습니다.


이야기

문자열 파서 라이브러리에서는 러스트의 엄격한 소유권 시스템 덕분에 이중 해제 오류를 피할 수 있었습니다. C++의 유사 라이브러리는 찾기 어려운 버그와 메모리 누수를 초래했습니다.