콘텐츠로 이동

Rust 문자열

텍스트를 다루는 부분은 Rust가 다른 언어와 다르다고 처음 느끼게 되는 곳 중 하나예요. 가장 큰 이유는 Rust에 문자열 타입이 두 가지나 있기 때문이에요. 왜 그런지 알고 나면 더는 헷갈리지 않아요.

  • &str(“문자열 슬라이스”) - 이미 어딘가에 있는 텍스트예요. 보통 코드에 직접 적어 넣은 텍스트가 여기에 해당해요. 크기가 고정되어 있어서 늘릴 수 없어요.
  • String - 소유권(ownership)을 가진, 늘릴 수 있는 텍스트예요. 내용을 덧붙일 수 있고, 힙(heap)에 저장돼요.
fn main() {
let literal: &str = "안녕"; // 문자열 슬라이스
let owned: String = String::from("안녕"); // 소유권을 가진 String
println!("{literal} / {owned}");
}
▶ 직접 실행해 보기

간단한 기준을 하나 알려 드릴게요. 고정된 텍스트를 출력하기만 한다면 "이렇게" 쓰는 것(&str)으로 충분해요. 실행 중에 텍스트를 만들거나, 늘리거나, 고쳐야 한다면 String을 쓰세요.

fn main() {
let mut greeting = String::from("안녕하세요");
greeting.push_str(", 세상"); // &str 덧붙이기
greeting.push('!'); // 문자 하나 덧붙이기
println!("{greeting}");
}

실행 결과:

안녕하세요, 세상!
▶ 직접 실행해 보기

문자열은 +로 합칠 수도 있고, format!을 쓸 수도 있어요. 보통은 format! 쪽이 더 읽기 쉬워요.

fn main() {
let first = String::from("안녕하세요, ");
let second = String::from("세상!");
let combined = first + &second; // second 앞의 &에 주목하세요
println!("{combined}");
// format!이 더 읽기 쉬울 때가 많아요.
let name = "Ferris";
let greeting = format!("안녕하세요, {name}!");
println!("{greeting}");
}
▶ 직접 실행해 보기

자주 쓰게 될 메서드 몇 가지예요. 이 예제는 .len()으로 바이트 수를 세기 때문에 문자열을 영어로 두었어요. 한글은 한 글자가 UTF-8로 3바이트라서 결과가 달라지거든요.

fn main() {
let text = " Hello, Rust! ";
println!("{}", text.trim()); // "Hello, Rust!" (앞뒤 공백 없음)
println!("{}", text.to_uppercase()); // " HELLO, RUST! "
println!("{}", text.to_lowercase()); // " hello, rust! "
println!("{}", text.contains("Rust")); // true
println!("{}", text.trim().len()); // 12 (바이트 단위 길이)
println!("{}", text.trim().replace("Rust", "World")); // "Hello, World!"
}
▶ 직접 실행해 보기

문자를 하나씩 살펴보려면 .chars()를 쓰세요.

fn main() {
for c in "abc".chars() {
println!("{c}");
}
}
▶ 직접 실행해 보기

누군가 입력 양식에 " rUsT is FUN "이라고 적었어요. 이것을 rust is great로 바꿔 보세요. 앞뒤 공백을 없애고, 모두 소문자로 바꾸고, fun을 great로 바꾸면 돼요. 그리고 결과와 그 길이를 출력하세요. 힌트: 문자열 메서드는 text.trim().to_lowercase()...처럼 줄줄이 이어서 호출할 수 있어요.

정답 보기
fn main() {
let input = " rUsT is FUN ";
// 먼저 trim, 그다음 소문자로, 그다음 (이제 소문자가 된) "fun"을 바꿔요
let clean = input.trim().to_lowercase().replace("fun", "great");
println!("{clean}");
println!("{}바이트", clean.len());
}

실행 결과:

rust is great
13바이트
▶ 직접 실행해 보기

순서가 중요해요. .to_lowercase()보다 .replace("fun", "great")를 먼저 호출하면 아무것도 바뀌지 않아요. 그 시점에는 텍스트가 아직 FUN이기 때문이에요.

first = "Ada"와 last = "Lovelace"가 주어졌을 때, [email protected]이라는 String을 만들어 출력하세요. 그다음 그 안에 @가 들어 있는지도 출력하세요. 힌트: format!의 {} 자리에는 first.to_lowercase() 같은 식을 인자로 넣을 수 있어요.

정답 보기
fn main() {
let first = "Ada";
let last = "Lovelace";
let email = format!("{}.{}@example.com", first.to_lowercase(), last.to_lowercase());
println!("{email}");
println!("올바른 형식인가요: {}", email.contains('@'));
}

실행 결과:

올바른 형식인가요: true
▶ 직접 실행해 보기
  • &str은 빌려 온 고정된 텍스트이고, String은 소유권을 가지며 늘릴 수 있어요.
  • String은 .push_str()(텍스트)이나 .push()(문자 하나)로 늘려요.
  • 문자열을 합칠 때는 +보다 format!을 쓰세요. 더 읽기 쉽고 아무것도 이동시키지 않아요.
  • 자주 쓰는 메서드: .trim(), .to_uppercase(), .contains(), .replace(), .chars().

이해도 점검

1. 소유권을 가지고, 실행 중에 늘릴 수 있는 문자열 타입은 무엇인가요?

2. 여러 문자열을 합칠 때 이 페이지에서 +보다 format!을 권하는 이유는 무엇인가요?

3. Rust 문자열을 text[0]처럼 인덱싱할 수 없는 이유는 무엇인가요?

4. greeting.push('!')처럼 String에 문자 하나를 덧붙이는 메서드는 무엇인가요?

점수: 0 / 4