📝
String vs &str — Ruby에는 없는 문자열의 두 얼굴
소유하는 문자열과 빌리는 문자열의 차이
Ruby에서 문자열은 간단하다. String 하나. 만들고, 바꾸고, 넘기면 된다. Rust에서는 문자열이 두 종류다.
String — 소유하는 문자열
let s = String::from("hello");
let s = "hello".to_string(); // 같은 결과
힙에 할당된다. 크기가 변할 수 있다. Ruby의 일반 String과 가장 비슷하다.
&str — 빌리는 문자열
let s: &str = "hello"; // 문자열 리터럴은 항상 &str
어딘가에 있는 문자열 데이터의 참조(슬라이스)다. 크기를 바꿀 수 없다. Ruby의 frozen string에 가깝다.
언제 뭘 쓰나
함수 인자로 받을 때 → &str
// ✅ 좋음 — String도 &str도 받을 수 있다
fn greet(name: &str) {
println!("Hello, {}!", name);
}
greet("world"); // &str 전달
greet(&my_string); // String → &str 자동 변환
// ❌ 불필요하게 제한적
fn greet(name: String) { // String만 받음
println!("Hello, {}!", name);
}
구조체 필드에 저장할 때 → String
struct User {
name: String, // 소유해야 하니까 String
}
변환
// &str → String
let owned: String = "hello".to_string();
let owned: String = String::from("hello");
let owned: String = "hello".to_owned();
// String → &str
let borrowed: &str = &owned;
let borrowed: &str = owned.as_str();
문자열 연결 — Ruby보다 귀찮다
Ruby의 "Hello, " + name이나 "Hello, #{name}"은 간단하다. Rust는 좀 다르다.
// format! — Ruby의 "#{}"/sprintf에 해당
let msg = format!("Hello, {}!", name);
// + 연산자 — 왼쪽이 String이어야 함
let msg = String::from("Hello, ") + name;
// push_str — String에 &str 추가
let mut msg = String::from("Hello, ");
msg.push_str(name);
대부분 format!을 쓰면 된다. Ruby의 문자열 보간과 가장 비슷하다.
왜 두 종류인가
성능과 소유권. &str은 복사 없이 기존 문자열 데이터를 가리킨다. 함수에 문자열을 넘길 때 힙 할당 없이 참조만 전달할 수 있다. 이게 Rust가 빠른 이유 중 하나.
핵심 포인트
1
String = 힙 할당, 가변 / &str = 참조, 불변
2
함수 인자는 &str로 받는 게 관례 — String도 자동 변환됨
3
구조체 필드는 String — 소유권이 필요하니까
4
format!()이 Ruby의 문자열 보간(#{})에 가장 가깝다
장점
- ✓ 불필요한 문자열 복사를 &str로 회피 — 성능 이점
- ✓ String과 &str의 구분이 소유권 이해의 좋은 입구
단점
- ✗ \"hello\".to_string() 같은 변환이 번거롭다
- ✗ 구조체에 &str을 넣으려면 라이프타임 명시가 필요 — 초보자 킬러
사용 사례
API 응답 파싱 — JSON에서 꺼낸 문자열 처리
설정 파일 읽기 — 파일 내용을 String으로, 파싱 중 &str로