프로그래밍Rust 개발자, 데이터 엔지니어

Rust에서 동적 배열(Vec)과 그 할당이 어떻게 구현되어 있는지 설명해 주세요. 요소를 추가/삭제할 때 어떤 어려움이 발생할 수 있으며, 불필요한 할당을 피하는 방법은 무엇인가요?

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

답변

Vec<T>는 동적이고, 확장 가능한 배열로, 요소를 단일 할당된(힙에) 메모리 블록에 저장합니다. 새 요소를 추가(push)할 때 길이가 증가하며, 필요에 따라 새로운 메모리가 할당(재할당)됩니다. push 시 내부적으로 용량(capacity)을 지수적으로 증가시켜 지속적인 재할당을 피합니다. 요소를 삭제(pop/remove)할 때 용량은 자동으로 줄어들지 않습니다.

자주 발생하는 문제는 지속적인 추가 시 과도한 할당 및 재할당입니다.

사전 할당 예시:

let mut v = Vec::with_capacity(1000); for i in 0..1000 { v.push(i); } assert_eq!(v.capacity(), 1000);

문제의 함정

질문: v.shrink_to_fit() 호출 후 vec의 capacity는 어떻게 될까요? 길이와 같아질까요?

잘못된 답변: 네, 항상, shrink_to_fit 이후 capacity == len.

올바른 답변: 꼭 그런 것은 아닙니다. shrink_to_fit의 구현은 할당자에게 "요청"입니다. 일반적으로 가능한 최소 용량으로 시도하지만, 할당자의 구현에 따라 다를 수 있습니다(예: 길이보다 클 수 있음).

예시:

let mut v = Vec::with_capacity(10); for i in 0..5 { v.push(i); } v.shrink_to_fit(); // capacity ≥ len (5), 하지만 꼭 == len이라고 보장되지 않음

주제에 대한 세부 사항을 알지 못해 발생한 실제 오류 예시


이야기

개발자가 capacity를 설정하지 않고 Vec에 객체를 여러 번 푸시 하여, 대량 데이터에서 재할당이 기하급수적으로 증가해 전체 처리가 "무거운" 루프에서 지연되었습니다. with_capacity로 최적화하니 10배로 시간이 단축되었습니다.


이야기

팀은 매 pop() 후에 shrink_to_fit를 정기적으로 호출하여 메모리를 절약하려고 했습니다. 결국, 지속적인 재할당/해제가 발생하여 성능이 저하되어 서비스가 거의 DoS에 도달할 뻔했습니다.


이야기

Vec를 구조체의 내부 요소에 대한 참조로 필드로 남겨두었습니다. 재할당(push가 capacity를 초과할 때) 후 참조가 무효화되어 발생한 버그는 production에서 실행될 때까지 추적하기 어려웠습니다.