Rust의`String`과`str`의 차이점은 무엇입니까?


418

녹은 왜 String있고 str? 의 차이점은 무엇입니까 String와는 str? 언제 String대신에 str그 반대로 사용합니까? 그들 중 하나가 더 이상 사용되지 않습니까?

답변:


489

String동적 힙 문자열 유형 Vec입니다. 문자열 데이터를 소유하거나 수정해야 할 때 사용하십시오.

str메모리 어딘가에 동적 길이의 UTF-8 바이트의 불변의 1 시퀀스입니다. 크기를 알 수 없으므로 포인터 뒤에서 만 처리 할 수 ​​있습니다. 이 수단 str가장 일반적으로 같이 나타납니다 &str: 일부 UTF-8 데이터에 대한 참조는 일반적으로 "문자열 슬라이스"또는 단지 "조각"이라고합니다. 슬라이스 는 일부 데이터에 대한보기 일 뿐이며 해당 데이터는 어디든있을 수 있습니다 (예 :

  • 정적 저장소에서 : 문자열 리터럴 "foo"은입니다 &'static str. 데이터는 실행 파일로 하드 코딩되고 프로그램이 실행될 때 메모리로로드됩니다.
  • 할당 된 힙 내부String : 의 데이터 보기String역 참조합니다&strString .
  • 스택에서 : 예를 들어 다음은 스택 할당 바이트 배열을 만든 다음 해당 데이터&str뷰를 다음과 같이 가져옵니다 .

    use std::str;
    
    let x: &[u8] = &[b'a', b'b', b'c'];
    let stack_str: &str = str::from_utf8(x).unwrap();
    

요약하면, String문자열을 다른 스레드에 전달하거나 런타임에 빌드하는 등 소유 한 문자열 데이터가 필요한 경우 사용 하고 문자열 &str보기 만 필요한 경우 사용 하십시오.

이것은 벡터 Vec<T>와 슬라이스 &[T]사이의 관계와 동일 하며 일반 유형에 대한 값별 T및 참조 별 관계와 유사합니다 &T.


1 A는 str고정 길이이다; 끝을 넘어서 바이트를 쓰거나 뒤에 유효하지 않은 바이트를 남겨 둘 수 없습니다. UTF-8은 가변 너비 인코딩이기 때문에 str많은 경우 효과적으로 모든 s를 변경할 수 없습니다. 일반적으로, 돌연변이는 이전보다 더 많거나 적은 바이트를 쓰도록 요구합니다 (예를 들어 a(1 바이트)를 ä(2+ 바이트)로 바꾸려면 더 많은 공간을 확보해야합니다 str). &str제자리를 수정할 수있는 특정 방법이 있습니다 . 대부분 ASCII 문자 만 처리 하는 방법이 있습니다 make_ascii_uppercase.

(2 개) 동적 크기 유형 과 같은 것들을 허용 Rc<str>녹 1.2 이후 UTF-8 바이트 카운트 기준의 시퀀스를. Rust 1.21을 사용하면 이러한 유형을 쉽게 만들 수 있습니다.


10
"UTF-8 바이트 시퀀스 ( 알 수없는 길이 )"-이 날짜가 오래 되었습니까? 문서는 "A가 말하는 &str두 가지 구성 요소로 구성됩니다 : 일부 바이트에 대한 포인터와 길이를."
mrec

11
구식이 아니며 (표현이 상당히 안정적 임) 약간 부정확합니다. 정적으로 알려지지 않았습니다 [u8; N].
huon

2
@mrec 컴파일 타임에 알 수 없으므로 스택 프레임을 만들 때 크기에 대한 가정을 할 수 없습니다. 따라서 왜 종종 참조로 취급 되는가, 참조 시점은 컴파일시 알려진 크기, 포인터의 크기입니다.
Sekhat

1
업데이트 : Rc<str>그리고 Arc<str>지금은 표준 라이브러리를 통해 사용할 수 있습니다.
Centril

1
@cjohansson 정적으로 할당 된 객체는 일반적으로 힙이나 스택이 아니라 자체 메모리 영역에 저장됩니다.
Brennan Vincent

96

나는 C ++ 배경이 내가 생각하는 것이 매우 유용하다고 String&strC ++ 측면에서 :

  • 녹은 String같다 std::string; 메모리를 소유하고 메모리를 관리하는 더티 작업을 수행합니다.
  • 녹는 &str유사한이다 char*(그러나 좀 더 정교한); 의 내용에 대한 포인터를 얻는 것과 같은 방식으로 청크의 시작을 가리 킵니다 std::string.

둘 다 사라질까 요? 난 그렇게 생각하지 않아. 그들은 두 가지 목적을 제공합니다.

String버퍼를 유지하고 사용하는 것이 매우 실용적입니다. &str가벼우 며 문자열을 "보는"데 사용되어야합니다. 새 메모리를 할당하지 않고도 청크를 검색, 분할, 구문 분석 및 교체 할 수 있습니다.

&strString문자열 리터럴을 가리킬 수 있으므로 내부를 볼 수 있습니다. 다음 코드는 리터럴 문자열을 String관리 메모리 에 복사해야합니다 .

let a: String = "hello rust".into();

다음 코드를 사용하면 리터럴 자체를 복사하지 않고 사용할 수 있습니다 (읽기 전용).

let a: &str = "hello rust";

12
string_view처럼?
Abhinav Gauniyal

1
예, string_view와 비슷하지만 언어에 내재되어 있으며 올바르게 빌려 왔습니다.
locka

41

str로만 사용되는 &str문자열 슬라이스는 UTF-8 바이트 배열에 대한 참조입니다.

String~str확장 가능한 소유 UTF-8 바이트 배열이었습니다.


기술적으로, 어떤 일에 사용하는 것은 ~str지금Box<str>
jv110은

3
@ jv110 : 아니, 때문에 ~str확장 가능한 반면이 없었다 Box<str>확장 가능한 없습니다. (즉 ~str하고 ~[T], 마술 가변 장이었다 다른 달리 ~-object, 정확히 왜 String하고 Vec<T>그래서 규칙이 모두 간단하고 일관성이 있다고 소개했다.)
크리스 모건

18

그들은 실제로 완전히 다릅니다. 우선, a str는 유형 수준에 지나지 않습니다. 소위 동적 크기 유형 (DST)이기 때문에 유형 수준에서만 추론 할 수 있습니다. str테이크 업 크기는 컴파일 타임에 알 수 없으며 런타임 정보에 따라 다릅니다. 컴파일러는 컴파일 타임에 각 변수의 크기를 알아야하기 때문에 변수에 저장할 수 없습니다. A str는 개념 상 단지 u8UTF-8의 유효한 형식을 보장하는 바이트 행입니다 . 행이 얼마나 큽니까? 런타임까지 아무도 모르므로 변수에 저장할 수 없습니다.

흥미로운 점은 런타임에 &str또는 기타 유사한 포인터 존재 한다는 str것 입니다. 소위 "팻 포인터"입니다. 추가 정보 (이 경우 가리키는 대상의 크기)가있는 포인터이므로 두 배가 큽니다. 실제로 a 는 a 에 가깝지만 (a는 아님). A 는 두 단어입니다. a의 첫 바이트에 대한 하나의 포인터와를 나타내는 바이트 수를 나타내는 다른 숫자 .Box<str> &strString&String&strstrstr

말한 것과 달리, a str는 불변 일 필요는 없다. 에 &mut str대한 배타적 포인터로를 얻을 수 있다면 str, 그것을 바꿀 수 있고 그것을 바꿀 수있는 모든 안전한 함수는 UTF-8 제약 조건이 유지되도록 보장합니다. 왜냐하면 위반되면 라이브러리 가이 제약 조건이 true이며 확인하지 않습니다.

그래서 무엇 String입니까? 그것은 단어입니다. 두 개는 동일 &str하지만 str힙에 버퍼 의 용량 인 세 번째 단어를 추가합니다. 항상 힙에 str채워져 있기 전에 항상 힙 (a 는 반드시 힙에있는 것은 아님)으로 채워져 다시 할당해야합니다. String기본적으로 소유str그들이 말하는대로; 그것은 그것을 제어하고 크기를 조정하고 적합하다고 판단되면 재 할당 할 수 있습니다. 따라서 a String는 a &str보다 가까이에 str있습니다.

또 다른 것은 Box<str>; 이것은 또한 a를 소유 str하고 런타임 표현은 a 와 동일 &str하지만 그것 str과 는 달리 소유 &str하지만 용량을 모르기 때문에 크기를 조정할 수 없으므로 기본적으로 크기를 조정할 수없는 Box<str>고정 길이로 볼 String수 있습니다 String크기를 조정하려면 항상로 변환하십시오 ).

매우 유사한 관계 사이에 존재 [T]하고 Vec<T>아무 UTF-8 제약이없고, 그 크기가 동적 아닌 임의의 유형을 지정할 수 제외.

str타입 레벨에서 의 사용 은 주로 &str; 특성을 편리하게 쓸 수 있도록 유형 수준에 존재합니다. 이론적 str으로는 유형의 것이 존재할 필요는 &str없었지만 이제는 일반적 일 수있는 많은 추가 코드를 작성해야합니다.

&strString복사하지 않고 여러 개의 서로 다른 하위 문자열을 가질 수있는 것이 매우 유용합니다 . 등은 말했다 String 소유str가 관리 힙에 당신 만의 문자열을 만들 수 있다면 String새로운으로 String는 녹에 모든 전용 메모리 안전을 다루는 하나 개의 소유자를 가질 수 있기 때문에 복사해야합니다. 예를 들어 문자열을 슬라이스 할 수 있습니다.

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

str동일한 문자열의 두 개의 다른 하위 문자열이 있습니다. 힙 string에서 실제 전체 str버퍼 를 소유하고 &str하위 문자열은 힙 에서 해당 버퍼에 대한 팻 포인터입니다.


4

std::String단순히의 벡터입니다 u8. 소스 코드 에서 정의를 찾을 수 있습니다 . 힙 할당 및 확장 가능합니다.

#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
    vec: Vec<u8>,
}

strstring slice 라고도하는 기본 유형 입니다. 문자열 슬라이스의 크기는 고정되어 있습니다. 리터럴 문자열 let test = "hello world"에는 &'static str유형이 있습니다. test이 정적으로 할당 된 문자열에 대한 참조입니다. &str예를 들어, 수정할 수 없습니다

let mut word = "hello world";
word[0] = 's';
word.push('\n');

str&mut str예를 들어 다음과 같이 가변 슬라이스가 있습니다 . pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)

let mut s = "Per Martin-Löf".to_string();
{
    let (first, last) = s.split_at_mut(3);
    first.make_ascii_uppercase();
    assert_eq!("PER", first);
    assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);

그러나 UTF-8을 조금만 변경하면 바이트 길이가 변경 될 수 있으며 슬라이스는 해당 참조를 재 할당 할 수 없습니다.


0

간단히 말해서, String데이터 유형은 (와 마찬가지로 Vec) 힙에 저장 되며 해당 위치에 액세스 할 수 있습니다.

&str슬라이스 유형입니다. 즉 String, 힙에 이미 존재한다는 의미 입니다.

&str런타임에 할당을 수행하지 않습니다. 따라서 메모리상의 이유로 &strover 을 사용할 수 있습니다 String. 그러나 사용시 &str명시 적 수명을 다룰 수 있음을 명심하십시오 .


1
힙 어딘가 – 완전히 정확하지는 않습니다.
Shepmaster

내가 의미하는 바 strview이미 String힙에 존재 한다는 것 입니다 .
00imvj00

1
나는 그것이 당신이 의미하는 것을 이해하고 있으며, 그것이 완전히 정확하지 않다고 말하고 있습니다. "힙"은 진술의 필수 부분이 아닙니다.
Shepmaster

-1

C # 및 Java 사용자의 경우 :

  • 녹 ' String===StringBuilder
  • 녹의 &str === (불변) 문자열

나는 &strJava / C #의 인터 닝 된 문자열과 같이 문자열을 볼 수있는 뷰로 생각 하고 싶습니다. 변경 할 수 없으며 새로운 것을 생성하십시오.


1
Java / C # 문자열과 Rust 문자열의 가장 큰 차이점은 Rust가 문자열을 올바른 유니 코드로 보증한다는 점입니다. 따라서 문자열에서 세 번째 문자를 얻으려면 "abc"보다 더 많은 생각이 필요합니다 [2]. (우리가 다국어 세계에 살고 있기 때문에 이것은 좋은 일입니다.)
Squirrel

이것은 잘못된 것 입니다. 가변성에 관한 주제는 이미 투표권이 높은 답변에서 다루어졌습니다. 자세한 내용은 읽어보십시오.
Shepmaster

-5

다음은 빠르고 쉬운 설명입니다.

String-확장 가능하고 소유 가능한 힙 할당 데이터 구조. 에 강제 할 수 있습니다 &str.

str-힙 (heap) 또는 이진 (binary)에있는 가변 길이의 고정 길이 문자열 (현재 Rust가 발전함에 따라)입니다. 과 str같은 문자열 슬라이스보기를 통해서만 빌린 유형으로 상호 작용할 수 있습니다 &str.

사용 고려 사항 :

String문자열을 다른 스레드에 전달하는 등 문자열을 소유하거나 변경하려는 경우 선호 하십시오.

&str문자열의 읽기 전용보기를 원하는 경우 선호 하십시오.


이것은 잘못된 것 입니다. 가변성에 관한 주제는 이미 투표권이 높은 답변에서 다루어졌습니다. 자세한 내용은 읽어보십시오.
Shepmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.