08. 슬라이스 — &str 와 &[T]
슬라이스는 컬렉션의 일부를 빌려서 보는 참조형 뷰입니다. &str 은 String 의 일부 또는 문자열 리터럴 그 자체이고, &[T] 는 Vec<T> 나 배열의 일부에 대한 참조입니다. 슬라이스가 어떻게 함수 인자의 유연성을 폭발적으로 늘려 주는지, 그리고 왜 함수 인자에 String 보다 &str 을 받는 것이 idiomatic 인지 살펴봅니다.
이 강의에서 배우는 것
- 1&str 과 &[T] 슬라이스를 만들고 인덱싱한다
- 2함수 인자로 &str 을 받아 호출자 유연성을 높인다
- 3슬라이스가 (ptr, len) 으로 표현되는 fat pointer 임을 안다
- 4문자열 리터럴이 &'static str 임을 안다
- 5슬라이스 범위를 잘못 잡았을 때의 panic 위치를 안다
소개
함수가 String 을 인자로 받으면 호출자는 매번 새 String 을 만들어야 합니다. 문자열 리터럴 `"hello"` 도 직접 못 넘기죠. 슬라이스가 이 문제를 풉니다 — &str 으로 받으면 String 도 리터럴도 모두 통과.
핵심 개념
1) 슬라이스 = 참조형 뷰
슬라이스는 **데이터 + 길이** 를 가리키는 fat pointer (16바이트 — 포인터 8 + 길이 8) 입니다. 데이터를 소유하지 않고 빌려 봅니다.
2) &str — 문자열 슬라이스
- 문자열 리터럴 `"hello"` 의 타입이 `&'static str` — 프로그램 전체 수명
- `&s[0..5]` 처럼 String 의 일부도 슬라이스로 만들 수 있음
- **UTF-8 문자 경계** 가 아닌 인덱스로 자르면 런타임 panic
3) &[T] — 배열·Vec 슬라이스
- `&v[1..4]` 처럼 부분 슬라이스
- 함수 인자로 `fn sum(v: &[i32])` 식으로 받으면 배열·Vec 모두 통과
4) 함수 시그니처 idiom
라이브러리 함수를 만들 때 **String 보다 &str, Vec<T> 보다 &[T]** 를 받는 것이 idiomatic. 호출자가 더 다양한 입력을 넘길 수 있고, 불필요한 클론을 피합니다.
핵심 예제
String 과 리터럴 모두 받는 함수 — &str:
fn shout(s: &str) {
println!("{}!", s.to_uppercase());
}
fn main() {
let owned = String::from("hello");
shout(&owned); // String → &str (Deref)
shout("world"); // 리터럴 그대로
}슬라이스 만들기·인덱싱:
fn main() {
let s = String::from("hello world");
let hello: &str = &s[0..5];
let world: &str = &s[6..11];
println!("{} / {}", hello, world);
let arr = [10, 20, 30, 40, 50];
let mid: &[i32] = &arr[1..4]; // [20, 30, 40]
println!("{:?}", mid);
}Vec 슬라이스를 받는 일반 함수:
fn sum(v: &[i32]) -> i32 {
let mut s = 0;
for x in v { s += x; }
s
}
fn main() {
let v = vec![1, 2, 3, 4, 5];
println!("{}", sum(&v)); // Vec → &[i32]
println!("{}", sum(&[10, 20])); // 배열 → &[i32]
println!("{}", sum(&v[1..4])); // 슬라이스
}자주 하는 실수
Q. 한글 문자열을 슬라이스하면 panic 나요
A. UTF-8 에서 한글 한 글자는 3바이트입니다. `&"안녕"[0..1]` 은 글자 중간을 자르므로 panic. 문자 단위로 다루려면 `.chars()` 이터레이터를 쓰세요.
Q. String 을 인자로 받으면 안 되나요?
A. 됩니다만, &str 이 더 일반적입니다. 함수가 소유권을 가져야 하는 경우(저장해 둘 때 등) 만 String 으로 받으세요.
Q. 슬라이스의 길이가 컴파일 타임에 정해지나요?
A. 아닙니다. 런타임에 결정되므로 fat pointer 가 길이를 함께 갖고 다닙니다. 그래서 `usize` 한 워드의 길이 정보가 포함돼 일반 참조보다 두 배 크기.
정리
- 슬라이스는 (포인터, 길이) 의 참조형 뷰
- &str / &[T] — 문자열 / 임의 타입
- 함수 시그니처에 &str·&[T] 를 쓰면 더 많은 호출이 자연스럽게 통과
- UTF-8 경계가 아닌 위치에서 자르면 런타임 panic
과제
- 문장에서 첫 단어만 잘라 반환하는 fn first_word(s: &str) -> &str 작성
- Vec<i32> 의 최대값을 찾는 함수를 &[i32] 인자로 작성
- 한글 문자열에서 .chars().count() 와 .len() 의 차이를 출력해 비교