문자열
Rust에서 가장 많이 사용되는 두 가지 문자열 타입은 String과 &str입니다.
String은 바이트 벡터(Vec<u8>)로 저장되지만, 항상 유효한 UTF-8 시퀀스임이 보장됩니다. String은 힙에 할당되며, 크기 조절이 가능하고 널 종료(null terminated)되지 않습니다.
&str은 항상 유효한 UTF-8 시퀀스를 가리키는 슬라이스(&[u8])이며, &[T]가 Vec<T>를 들여다보는 창(view)인 것과 마찬가지로 String을 들여다보는 데 사용될 수 있습니다.
fn main() {
// (모든 타입 어노테이션은 불필요합니다)
// 읽기 전용 메모리에 할당된 문자열에 대한 참조
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("팬그램(Pangram): {}", pangram);
// 단어들을 역순으로 순회합니다. 새로운 문자열은 할당되지 않습니다
println!("역순 단어들");
for word in pangram.split_whitespace().rev() {
println!("> {}", word);
}
// 문자들을 벡터로 복사하고, 정렬한 뒤 중복을 제거합니다
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
// 비어 있고 크기 조절 가능한 `String`을 생성합니다
let mut string = String::new();
for c in chars {
// 문자열 끝에 문자를 삽입합니다
string.push(c);
// 문자열 끝에 문자열을 삽입합니다
string.push_str(", ");
}
// 트림된 문자열은 원본 문자열에 대한 슬라이스이므로,
// 새로운 할당이 수행되지 않습니다
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str = string.trim_matches(chars_to_trim);
println!("사용된 문자들: {}", trimmed_str);
// 문자열을 힙에 할당합니다
let alice = String::from("I like dogs");
// 새로운 메모리를 할당하고 수정된 문자열을 그곳에 저장합니다
let bob: String = alice.replace("dog", "cat");
println!("앨리스가 말합니다: {}", alice);
println!("밥이 말합니다: {}", bob);
}
더 많은 str/String 메서드는 std::str 및 std::string 모듈에서 찾을 수 있습니다
리터럴과 이스케이프
특수 문자가 포함된 문자열 리터럴을 작성하는 방법은 여러 가지가 있습니다. 모두 비슷한 &str을 결과로 내므로 가장 작성하기 편리한 형식을 사용하는 것이 좋습니다. 마찬가지로 바이트 문자열 리터럴을 작성하는 방법도 여러 가지가 있으며, 모두 &[u8; N] 결과를 냅니다.
일반적으로 특수 문자는 백슬래시 문자 \로 이스케이프됩니다. 이 방법을 통해 출력 불가능한 문자나 입력 방법을 모르는 문자 등 어떤 문자든 문자열에 추가할 수 있습니다. 백슬래시 자체를 리터럴로 원한다면 백슬래시 하나를 더 붙여 \\와 같이 이스케이프하세요.
리터럴 내에 나타나는 문자열 또는 문자 리터럴 구분자는 반드시 이스케이프해야 합니다: "\"", '\''.
fn main() {
// 16진수 값을 사용하여 바이트를 작성하기 위해 이스케이프를 사용할 수 있습니다...
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("What are you doing\x3F (\\x3F는 ?를 의미합니다) {}", byte_escape);
// ...또는 유니코드 코드 포인트.
let unicode_codepoint = "\u{211D}";
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
println!("유니코드 문자 {} (U+211D)는 {}라고 불립니다",
unicode_codepoint, character_name );
let long_string = "문자열 리터럴은
여러 줄에 걸쳐 있을 수 있습니다.
여기서의 줄바꿈과 들여쓰기 ->\
<- 역시 이스케이프될 수 있습니다!";
println!("{}", long_string);
}
때때로 이스케이프해야 할 문자가 너무 많거나 문자열을 있는 그대로 작성하는 것이 훨씬 더 편리할 때가 있습니다. 이때 로우(raw) 문자열 리터럴이 사용됩니다.
fn main() {
let raw_str = r"이스케이프가 여기선 작동하지 않습니다: \x3F \u{211D}";
println!("{}", raw_str);
// 로우 문자열에서 따옴표가 필요하다면, # 쌍을 추가하세요
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// 문자열 내에 "#가 필요하다면, 구분자에 더 많은 #를 사용하세요.
// 최대 255개의 #를 사용할 수 있습니다.
let longer_delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", longer_delimiter);
}
UTF-8이 아닌 문자열을 원하시나요? (str과 String은 반드시 유효한 UTF-8이어야 함을 기억하세요). 아니면 대부분이 텍스트인 바이트 배열을 원하시나요? 바이트 문자열이 해결해 드립니다!
use std::str;
fn main() {
// 이는 실제로 `&str`이 아님에 유의하세요
let bytestring: &[u8; 21] = b"this is a byte string";
// 바이트 배열은 `Display` 트레이트를 가지고 있지 않으므로, 출력에 다소 제한이 있습니다
println!("바이트 문자열: {:?}", bytestring);
// 바이트 문자열은 바이트 이스케이프를 가질 수 있습니다...
let escaped = b"\x52\x75\x73\x74 as bytes";
// ...하지만 유니코드 이스케이프는 허용되지 않습니다
// let escaped = b"\u{211D} is not allowed";
println!("일부 이스케이프된 바이트: {:?}", escaped);
// 로우 바이트 문자열은 로우 문자열과 똑같이 작동합니다
let raw_bytestring = br"\u{211D} is not escaped here";
println!("{:?}", raw_bytestring);
// 바이트 배열을 `str`로 변환하는 것은 실패할 수 있습니다
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("그리고 텍스트로 변환하면: '{}'", my_str);
}
let _quotes = br#"일반 로우 문자열처럼, \
더 "화려한" 포맷팅을 사용할 수도 있습니다"#;
// 바이트 문자열은 UTF-8일 필요가 없습니다
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // SHIFT-JIS 인코딩의 "ようこそ"(환영합니다)
// 그런 경우에는 항상 `str`로 변환될 수는 없습니다
match str::from_utf8(shift_jis) {
Ok(my_str) => println!("변환 성공: '{}'", my_str),
Err(e) => println!("변환 실패: {:?}", e),
};
}
문자 인코딩 간의 변환에 대해서는 encoding 크레이트를 확인해 보세요.
문자열 리터럴을 작성하고 문자를 이스케이프하는 방법에 대한 더 자세한 목록은 Rust 레퍼런스의 ‘Tokens’ 장에 나와 있습니다.