이 글을 읽고 광명을 찾으시길 원하시는 분은 먼저 포인터와 스택, 힙에 대해 알아야 합니다.
군 복무시절 선임과 밤새도록 디아블로3를 플레이하던 시절이 그립..지는 않습니다.
먼저 아래 글을 읽고 오세요.
https://velog.io/@jay/stack-heap-pointer
당신은 왜 나를 이렇게 힘들게 하는가?😂😂
러스트에는 세 가지 종류의 타입으로 텍스트를 표현
할 수 있어요. 오늘 배울 String, &str, str이 바로 그것이에요.
String은 사이즈가 변경될 수 있는 owned 타입이에요. 사이즈가 변경될 수 있으니 힙에 저장되어야곘죠? 사이즈가 변경될 수 있으니 값이 변경될 수도 있어요.
스터얼~ 스털~ 이라고 읽어요. 스떠얼~ 이라고 읽는게 더 자연스러울지도 모르겠어요.
String의 일부분에 대한 레퍼런스에요. 레퍼런스니까 값을 가지지 않고 borrow만 하겠죠? 그리고 일부분이니까 slice라고 불러요. 즉 &str은 string slice라고 부르기도 해요.
레퍼런스이기 떄문에 값을 소유하고 있지는 않지만 String 타입이 가지고 있는 값에 접근해서 읽을 수는 있어요.
지금까지 아주 스무스하게 이해헀어요.
str은 사이즈가 정해지지 않은 문자열 타입이에요. &str에서 &하나만 뺐을 뿐인데 벌써 사이즈가 정해지지 않았다는 다소 난해한 문장이 등장했어요.
잠깐만 러스트 컴파일러와 관련된 이야기를 하면, 컴파일러는 특정 변수나 값에 대한 사이즈를 알아야 컴파일 할 수 있어요 그래서 힙에 있는 동적인 값을 컴파일하기 위해 레퍼런스를 참조해요. 레퍼런스는 그저 메모리 주소를 가르키는 개념일 뿐이기 때문에 사이즈가 고정되어있거든요. 그래서 &str은 레퍼런스이고 러스트 컴파일러가 컴파일하는데 아무 문제가 없어요.
4줄만에 설명을 했어요. 이정도면 꽤 설명을 잘했다고 할 수 있겠어요.
다시 돌아와서 &str은 사이즈가 정해졌지만 str은 사이즈가 정해지지 않은 타입이에요.
문자의 연속됨을 나타내고 문자열을 소유하지 않고 borrow 한다는 점에서는 &str과 비슷하지만 사이즈가 정해지지 않았다는 특징을 이용하는 경우가 있어요. 바로 제너릭이에요.
참내 내용이 계속 복잡해지고 있지만 조금만 더 읽어보도록 해요.
fn example_fn<T: AsRef<str>>(arg: T) {
let s = arg.as_ref();
println!("{}", s);
}
let s = "hello";
exmaple_fn(s);
네트워크나 파일등에서 가져오는 문자열은 런타임에만 그 사이즈를 알 수 있어요. 따라서 이 경우에 str을 사용할 수 있고 나중에 출력할 때 사이즈가 정해진 &str로 바꾸어 값에 접근할 수 있어요.
str은 size가 정해지지 않았다고 했죠?
이 말은 러스트 컴파일러가 컴파일 타임에 str의 사이즈를 알 수 없다는 뜻이에요. 고정되지 않은 타입이기 때문이죠. 런타임에 변경될 수 있는 str 사이즈이기 때문에 실행하기 전까지는 이 str 타입의 사이즈를 알 수 없어요.
str은 이러한 특성 때문에 함수에 인자로 바로 전달 될 수 없어요.
꼭 레퍼런스로 제공되어야 하기 때문에 str이 아닌 &str 혹은 &mut str 타입이 전달되어야 해요.
둘다 문자열을 표현하기 위한 타입이라는 공통점을 가지고 있지만 같지 않아요.
str은 unsized string type이기 때문에 str의 길이는 런타임에 변경될 수 있어요.
String은 as_str() 메소드 호출을 통해 &str 타입으로 변경될 수 있어요. 그리고 그 반대도 to_string() 메소드를 호출함으로 변경 가능해요.
String은 메모리를 소유하고 있는 owned type이지만 str은 String의 일부를 표현해요.
str은 String의 일부분을 가르키며 String type의 slice라고 부르기도 해요. 그리고 둘 다 힙에 저장되지만 &str은 스택에 저장됩니다.