슬라이스(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)을 초래했으며, 이는 안전한 슬라이스의 올바른 사용으로 예방될 수 있었습니다.