문제의 역사:
네이티브 언어(C/C++)와 비교할 때, 러스트는 참조 타입(&str)과 소유 타입(String) 간의 엄격한 분리를 통해 안전한 문자열 작업을 제공합니다. 이는 잘못된 메모리, 버퍼 오버플로우 및 더블 프리와 관련된 대부분의 오류를 제거합니다.
문제:
성숙한 GC 언어들과 달리, 러스트에서는 문자열이 누가 소유하고 있고, 얼마 동안 살아있으며, 수정 후에 어떤 잘못된 참조를 피해야 하는지를 명확히 이해해야 합니다. UTF-8 문자열 작업은 인덱싱과 수정할 때 주의가 필요합니다.
해결책:
러스트에서 String은 내용물을 소유하는 변경 가능한 힙 할당 문자열입니다. &str은 UTF-8 보장을 가진 바이트 시퀀스에 대한 변경 불가능한 참조입니다. 필요에 따라 std의 메서드를 사용하여 안전하게 변환할 수 있습니다 (&str -> String 및 그 반대).
코드 예시:
fn main() { let owned: String = String::from("Rust"); let borrowed: &str = &owned; let primitive: &str = "Hello"; // 리터럴은 항상 &str // &str -> String 변환 let s: String = primitive.to_string(); // String -> &str 변환 let st: &str = &s; println!("{} {} {} {}", owned, borrowed, primitive, st); }
주요 특징:
&'static str 타입을 가지며, String이 아닙니다.왜 문자열을 s[1] 또는 s[i]처럼 인덱싱할 수 없나요?
러스트의 문자열은 UTF-8이므로 직접적으로 인덱싱할 수 없습니다: s[i]는 i번째 문자를 반환하지 않으며, 때때로 잘못된 바이트 경계에 접근할 때 panic을 유발합니다. 대신, .chars().nth(i) 또는 .get(start..end) 메서드를 사용하세요.
&str를 안전하게 수정할 수 있나요?
안 됩니다 - &str은 항상 변경 불가능한 슬라이스입니다. 수정을 원할 경우 to_owned/to_string을 사용하거나 String/Vec<u8>을 사용하세요.
String::from("abc")와 "abc".to_string()의 원리는 무엇이 다른가요?
이 두 가지는 결과적으로 동등하며, 둘 다 &str에서 데이터를 복사하여 String을 생성합니다. 차이는 스타일에 있습니다: 예를 들어, to_string은 ToString 트레이트를 통해 구현되며, String::from은 "소유권 생성" 의도를 더 명확하게 표현합니다.
함수가 String을 받아 내부에서 불필요하게 문자열을 복사(clone)한 다음, 다른 함수에 slice를 쓰면서 원본의 수명을 늘리는 것을 잊었습니다. 결과: dangling reference & crash.
장점:
단점:
함수가 &str을 받아 수정이 필요할 경우 내부에서 .to_string()을 호출하고, 외부에서는 모든 로직이 "zero copy"로 유지됩니다. 수명은 제어되며, 불필요한 할당이 없습니다.
장점:
단점: