Rust는 핵심 언어 기능 내에서 한 가지 string 타입만 제공합니다. 바로 string slice인 str
입니다.
String
타입은 핵심 언어 기능 내에 구현된 것이 아닌 Rust의 표준 라이브러리를 통해 제공됩니다. 가변적이며, 커질 수 있고, 소유권을 갖으며 UTF-8로 인코딩된 string 타입입니다.
두 타입 모두 Rust 표준 라이브러리에서 매우 많이 사용됩니다. 또한 Rust 표준 라이브러리는 OsString
, OsStr
, CString
, CStr
과 같은 몇가지 다른 string 타입 또한 제공합니다.
let mut s = String::new();
//method 1
let data = "initial contents";
let s = data.to_string();
//method 2
let s = "initial contents".to_string();
//method 3
let s = String::from("initial contents");
let mut s = String::from("foo");
s.push_str("bar");
push_str
메소드는 string slice를 파라미터로 갖는데 이는 파라미터의 소유권을 가져올 필요가 없기 때문입니다. 따라서 아래의 코드 또한 실행 가능합니다.
let mut s = String::from("foo");
let s2 = "bar";
s1.push_str(&s2);
println!("s2 is {}", s2);
let mut s = String::from("lo");
s.push('l');
push
메소드는 한 개의 글자를 파라미터로 받아 String
에 추가합니다.
+
연산자와 format!
매크로를 이용한 접합let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1은 여기서 이동되어 더이상 쓸 수 없음을 유의
s1
이 더하기 연산 이후에 더이상 유효하지 않은 이유와 s2
의 참조자가 사용되는 이유는 +
연산자를 사용했을 때 호출되는 함수의 시그니처와 맞춰야 하기 때문입니다 +
연산자는 add
메소드를 사용하는데, 이 메소드의 시그니처는 아래와 같습니다.
fn add(self, s: &str) -> String {
만약 여러 String을 합치고자 한다면 +
의 동작은 다루기 불편해집니다.
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
이러한 복잡한 String 조합을 위해 format!
매크로를 사용할 수 있습니다. 이는 훨씬 읽기 쉬우며 파라미터의 소유권도 가져가지 않습니다.
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
Rust String은 인덱싱을 지원하지 않습니다. 이는 Rust가 어떻게 String을 메모리에 저장하는지에 관련되어 있습니다.
String
은 Vec<u8>
을 감싼 것입니다.
let len = String::from("Hola").len();
위의 경우, len
은 4가 됩니다. 이는 "Hola"를 저장하고 있는 Vec
이 4바이트 길이라는 뜻입니다.
let len = String::from("Здравствуйте").len();
위의 경우는 len
의 값이 12가 아닌 24가 됩니다. 이는 "Здравствуйте"를 UTF-8로 인코딩된 바이트들의 크기인데, 각각의 유니코드 스칼라 값이 2바이트를 차지하기 때문입니다. 따라서, String 바이트들 안의 인덱스는 유효한 유니코드 스칼라 값과 항상 대응되지는 않는 것입니다.
String 인덱싱을 더 구체적으로 하고 string slice를 원한다는 것을 가리키기 위해, []
에 숫자 하나를 사용하는 인덱싱보다 []
와 범위를 사용해 특정 바이트들이 담고 있는 string slice를 만들 수 있습니다.
let hello = "Здравствуйте";
let s = &hello[0..4];
여기서 s
는 String의 첫 4바이트를 담고 있는 &str
즉, "Зд"가 될 것입니다. 만약 범위를 [0..1]
로 지정했다면 패닉이 발생할 것입니다. 이 때문에 프로그램이 죽게될 수 있으므로 사용에 유의하여야 합니다.
char
, byte
메소드다행히, String 요소에 접근하는 다른 방법이 존재합니다.
만일 개별적인 유니코드 스칼라 값에 대한 연산을 수행하길 원한다면, 가장 좋은 방법은 char
메소드를 사용하는 것입니다. "Здравствуйте"에 대해 호출하면 char
타입의 12개의 값응로 나누어 반환하며, 각각의 요소에 접근하기 위해 결과값에 대해 iterate할 수 있습니다.
for c in "Здравствуйте".chars() {
println!("{}", c);
}
출력:
З
д
р
а
в
с
т
в
у
й
т
е
byte
메소드는 가공되지 않은 각각의 바이트를 반환합니다.
for b in "Здравствуйте".bytes() {
println!("{}", b);
}
출력:
208
151
208
// ... etc