Rust 소유권 설명해드림 3편
Slice
타입은 특정한 기능을 수행하는 레퍼런스의 한 종류입니다.
우리는 Slice
타입을 통해, 어떤 레퍼런스의 일부값만을 빌려올 수 있습니다.
예를 들어, 문자열 abcde
에서 마지막 글자의 레퍼런스를 가져오고 싶다면 다음과 같이 사용할 수 있습니다.
let name = "abcde";
let last_word = &name;
println!;
위 코드에서 볼 수 있듯이, Slice
타입은 대괄호 []
와 Range
를 나타내는 ..
를 이용해 레퍼런스를 빌려올 수 있습니다.
다만, Slice
타입은 바이트 단위를 기준으로 하기 때문에, 아래와 같이 유니코드 문자를 Slice
타입으로 가져올 수는 없습니다.
let name = "홍길동";
let last_word = &name;
println!; // Error!
Slice
타입 역시 레퍼런스이기 때문에, 소유권 2편에서 보았던 레퍼런스에 대한 규칙들이 동일하게 적용됩니다.
예를 들어, 아래와 같은 코드는 에러가 발생하는데요. 이유를 한 번 생각해보시길 바랍니다.
힙 메모리에 저장한 값을 소유한 변수나 이를 빌려온 스정가능한 변수가 값을 조작하기 위해서는, 이전에 정의한 모든 수정불가능한 레퍼런스는 사용완료한 상태여야 한다고 했죠?
현재 변수 greeting
에는 Slice
타입으로 빌려온 레퍼런스가 저장되어 있지만, s.clear()
함수 호출 전에 사용하진 않은 상태이기 때문에
컴파일이 되지 않습니다.
문자열 리터럴도 Slice
타입
다음과 같은 문자열 리터럴도 사실은 Slice
타입입니다.
let name = "홍길동";
let first_name = "길동";
let last_name = "홍";
위 코드를 구현하기 위해 rust는 힙 메모리 어딘가에 "홍길동" 혹은 이를 포함하는 몬자열을 가지고 있으며,
이 값에 대한 Slice
를 변수 name
, first_name
, last_name
에 빌려줍니다.
이를 통해, 작업 속도와 메모리 사용에 큰 이점을 갖게 됩니다.
그리고 이 레퍼런스는 수정불가능한 레퍼런스입니다. 문자열 리터럴은 수정불가능하다고 이야기한 이유가 이해되는 부분일 것입니다.
문자열 슬라이스를 나타내는 &str
String
타입을 수정하지 않는 경우에는 언제나 String Slice
를 사용하여 해당 문자열의 일부 혹은 전부를 빌려오는 것이 효율적입니다.
rust의 문자열 리터럴도 이 방식을 사용하고 있고요.
이 때문에, String Slice
를 위한 별도의 타입이 존재하는데, &str
이렇게 작성합니다.
우리는 앞으로 함수를 정의할 때 &String
대신 다음과 같이 &str
을 사용할 것입니다.
여기에는 String
의 레퍼런스와 슬라이스의 레퍼런스 둘 다 사용가능해서 유연성을 확보할 수 있기 때문입니다.
이번 포스트에서는 문자열 슬라이스만 살펴보았지만, 사실 슬라이스는 모든 컬렉션 타입에 적용됩니다.
앞으로 하나씩 알아보겠습니다!