Rust 슬라이스
Source: Dev.to

대부분의 Rust 관련 글은 JS/TS 개발자의 관점에서 씁니다. 직관적으로, 새로운 언어를 다른 언어를 통해 배울 때 두 언어 사이의 개념을 매핑하게 됩니다. 예를 들어, Rust의 struct는 TypeScript의 interface나 type에 매핑될 수 있습니다. 이렇게 하면 새로운 언어의 지식을 흡수하기가 쉬워집니다. 왜냐하면 개념이 갑자기 친숙하게 느껴지기 때문이죠.
하지만 이런 학습 패턴은 미묘한 문제를 일으킵니다: 아이디어를 계속 매핑하는 데 익숙해지면, 이미 알고 있는 언어에 직접적인 대응이 없는 새로운 언어의 개념을 진정으로 파악하기가 어려워집니다. 이것이 제가 Rust의 slice를 접했을 때 마주한 장애물입니다.
이 짧은 글은 참조, 빌림, 소유권과 같은 Rust 기본 개념에 대한 이해를 전제로 합니다.
일반적인 Rust 패턴
Rust에서는 인자를 변경할 필요가 없는 함수를 작성할 때 불변(읽기 전용) 참조를 사용하는 것이 일반적인 모범 사례입니다. 이를 예시로, 전체 이름을 받아서 이름만 반환하는 함수를 작성해 보겠습니다.
fn get_first_name(full_name: &str) -> &str {
full_name
.split_whitespace()
.next()
.unwrap_or("")
}
무엇이 일어나고 있나요?
- 함수는 문자열 슬라이스(
&str)를 빌려옵니다; 소유권을 가져오지는 않습니다. - 문자열을 공백을 기준으로 단어로 나눕니다.
- 첫 번째 단어(존재한다면)를 가져옵니다.
- 단어가 없을 경우
""를 반환합니다. - 새로운
String을 만들지 않고 원본 문자열의 슬라이스를 반환합니다.
풀어보자 😌😉
“슬라이스를 반환한다”는 실제로 무슨 의미일까요?
함수가 새로운 문자열을 만드는 것이 아니라, 기존 문자열의 일부를 가리키고 있을 뿐입니다.
원본 문자열이 "Lex Luthor"라면, 슬라이스는 다음을 가리킬 수 있습니다:
"Lex""Luthor""thor""ex Lu"- 혹은
"Lex Luthor"전체 (전체 문자열)
이 모든 것은 동일한 기본 문자열 데이터에 대한 뷰일 뿐입니다.
JavaScript 정신 모델 (도움이 됩니다)
If you’re coming from JavaScript, your brain probably jumps straight to substrings. A substring is a part of another string whose start and end indices fall within the bounds of the original string. If the start is 0 and the end is string.length, the substring represents the entire original string.
That’s conceptually how slices work in Rust too—but with one important difference:
Slices는 새로운 값이 아니라 참조입니다. This distinction is crucial.
슬라이스가 존재하는 이유
이미 문자열에 대한 참조가 있고 그 문자열의 일부만 참조하고 싶다면, 새로운 문자열을 할당하고 데이터를 복사할 수 있습니다. 그러면 다음과 같은 작업이 필요합니다:
- 새로운 메모리 할당
- 바이트 복사
- 실제로 필요하지 않은 추가 작업 수행
Rust는 말한다: 왜 원본 데이터의 일부만 참조하지 않겠는가? 복사도 없고, 새로운 할당도 없으며, 모든 것이 빠르고 명시적이다. 바로 이것이 슬라이스가 존재하는 이유다.
슬라이스는 문자열에만 해당되는 것이 아니다
슬라이스는 배열에서도 동작하며, 구문은 매우 유사합니다.
문자열과 함께
let original_value = String::from("Hello world");
let slice = &original_value[1..7];
slice는 original_value 안에 있는 "ello w"를 가리키는 &str입니다.
배열과 함께
let ages = [13, 21, 37, 4, 55];
let slice = &ages[2..4];
slice는 배열의 일부인 [37, 4]에 대한 참조입니다.
슬라이스는 &original_value[start_index..end_index] 형태를 사용하며, start_index는 포함, end_index는 제외됩니다. 인덱스 하나 또는 두 개를 생략할 수도 있습니다:
&value[..]→ 전체 값&value[..3]→ 시작부터 인덱스 2까지&value[3..]→ 인덱스 3부터 끝까지
최종 생각
Slices는 처음에 저에게 낯설게 느껴졌습니다. 왜냐하면 저는 그것들을 익숙한 JS 개념에 맞추려고 계속 애썼기 때문입니다. “새로운 값”이라는 관점에서 벗어나 기존 데이터에 대한 참조라는 관점으로 생각하기 시작하니 모든 것이 이해되었습니다.
더 깊이 파고들고 싶다면, 공식 Rust 책에서 slices를 아름답게 그리고 더 자세히 설명하고 있으며, slices를 사용해야 하는 강력한 예시도 포함하고 있습니다:
👉