프로그래밍백엔드 개발자

Rust에서 슬라이스 메커니즘은 어떻게 작동하며, 어떤 유형의 슬라이스가 존재하고, 안전성은 어떻게 보장되며, 슬라이스의 경계를 넘어설 때 무엇이 발생합니까?

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

답변

슬라이스(Slice)는 Rust에서 메모리에 연속적으로 배치된 컬렉션의 일부를 동적으로 표시하는 것입니다. 일반적인 슬라이스의 예는 &[T] 또는 &mut [T]입니다. 슬라이스는 데이터를 소유하지 않고 외부 버퍼를 참조합니다. 모든 슬라이스에는 링크와 함께 저장된 길이가 있습니다.

주요 유형:

  • &[T] — 변경 불가능한 슬라이스
  • &mut [T] — 변경 가능한 슬라이스
  • 문자열의 경우: &str (사실상 유효한 UTF-8인 바이트 슬라이스)

슬라이스의 안전성은 컴파일러와 런타임 수준에서 보장됩니다: 슬라이스의 경계를 넘어 요소에 접근하려고 하면 실행 시 패닉(panic)이 발생하며, C/C++와 같은 메모리의 불일치는 발생하지 않습니다.

예:

let v = vec![1, 2, 3, 4, 5]; let s: &[i32] = &v[1..4]; // 슬라이스, 요소 2, 3, 4를 참조 println!("{:?}", s); // [2, 3, 4] // s[3]; // panic! (경계 초과)

함정이 있는 질문

슬라이스가 비어 있을 수 있습니까? 빈 슬라이스와 None의 차이는 무엇입니까?

답변: 예, 슬라이스는 비어 있을 수 있습니다(&[]), 이는 길이가 0인 데이터 부분을 참조하지만 None과는 다릅니다. 빈 슬라이스는 안전하게 사용할 수 있으며, Option<&[T]>는 "슬라이스가 실제로 존재하는지 여부"를 구분하는 데 사용됩니다.

예:

let s: &[i32] = &[]; assert!(s.is_empty()); // 슬라이스가 완전히 없을 경우 Option<&[i32]> 사용

주제의 미세한 차이를 모르는 것에 따른 실제 오류 사례


이야기

대규모 로그 서비스에서 프로그래머가 사용자 데이터에서 동적으로 계산된 범위로 슬라이스를 가져왔습니다. 잘못된 슬라이스 계산(예: start > end 또는 end > len)이 발생했을 때, 테스트에서는 코드가 작동했지만 프로덕션에서는 "panic"을 유발하여 부하가 크게 걸린 순간 프로세스가 비정상 종료되었습니다.


이야기

내부 concurrent 해시 라이브러리에서 &mut [T]를 사용했으며, 여러 스레드가 하나의 배열의 서로 다른 슬라이스를 동시에 가져왔습니다. 한 스레드가 슬라이스를 수정했고 다른 스레드는 동일한 메모리에 대해 다른 슬라이스를 가져왔습니다. 프로그램은 컴파일되었으나 잘못된 분할로 인해 슬라이스가 겹쳤을 경우 UB(이상 동작)가 발생할 수 있었습니다.


이야기

네트워크 패킷을 구문 분석하는 시스템에서 슬라이스가 안전하지 않은 방식으로 생성되었습니다(raw pointer 및 from_raw_parts). 개발자는 입력 패킷의 길이가 올바른지 확인하는 것을 잊었습니다. 그 결과 경계를 넘어 읽으려는 시도가 애플리케이션 종료 및 취약성(potential OOB access)을 초래했으며, 이는 안전한 슬라이스의 올바른 사용으로 예방될 수 있었습니다.