Rustの `String`と` str`の違いは何ですか?


420

なぜRustにはStringandがあるのstrですか?違いは何ですかStringとはstrString代わりに、strまたはその逆をいつ使用しますか?それらの1つは廃止されていますか?

回答:


491

String動的ヒープ文字列型Vecです。たとえば、文字列データを所有または変更する必要がある場合に使用します。

strメモリ内の動的な長さのUTF-8バイトの不変の1シーケンスです。サイズは不明なので、ポインタの後ろでしか処理できません。これは、str最も一般的には2が次のように表示されることを意味し&strます。通常は「文字列スライス」または単に「スライス」と呼ばれる一部のUTF-8データへの参照。スライスは一部のデータのビューであり、そのデータはどこにでも配置できます。

  • 静的ストレージで"foo"は、文字列リテラルは&'static strです。データは実行可能ファイルにハードコードされ、プログラムの実行時にメモリに読み込まれます。
  • ヒープの中に割り当てられたStringStringへのデリファレンス&strビューStringのデータを。
  • スタック上:たとえば、次はスタックに割り当てられたバイト配列を作成し、そのデータのビューをとして&str取得します

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

要約すると、String所有する文字列データが必要な場合(文字列を他のスレッドに渡す、または実行時に作成するなど)と&str、文字列のビューのみが必要な場合に使用します。

これは、vector Vec<T>とslice &[T]の関係と同じであり、一般的な型の値Tによる参照と参照による関係に似てい&Tます。


1 A strは固定長です。末尾を超えてバイトを書き込んだり、末尾の無効なバイトを残したりすることはできません。UTF-8は可変幅エンコーディングstrなので、多くの場合、これによりすべてのが実質的に不変になります。一般に、ミューテーションでは、以前よりも多いまたは少ないバイト数を書き込む必要があります(たとえば、a(1バイト)をä(2+バイト)で置き換えると、にスペースを増やす必要がありますstr)。のように&strインプレースを変更できる特定のメソッドがあり、ほとんどがASCII文字だけを処理するものですmake_ascii_uppercase

2 動的にサイズ設定される型でRc<str>、Rust 1.2以降の参照カウントされたUTF-8バイトのシーケンスなどが可能です。Rust 1.21では、これらのタイプを簡単に作成できます。


10
「(不明な長さの)UTF-8バイトのシーケンス」-これは古くなっていますか?ドキュメントは、「Aが言う&str二つの成分で構成されています。いくつかのバイトへのポインタ、および長さを。」
mrec

11
それは古くはありません(その表現はかなり安定しています)、少し不正確です:とは異なり、静的に知られていません[u8; N]
huon

2
@mrecは、コンパイル時には不明です。たとえば、スタックフレームを作成するときは、サイズに関する仮定を行うことはできません。したがって、なぜそれがしばしば参照として扱われるのか、その参照はコンパイル時に既知のサイズであり、これはポインターのサイズです。
セハト2017

1
更新:標準ライブラリから使用できるようにRc<str>なりましたArc<str>
Centril 2018年

1
@cjohansson静的に割り当てられたオブジェクトは通常、ヒープにもスタックにも格納されず、独自のメモリ領域に格納されます。
ブレナンビンセント

97

私はC ++のバックグラウンドを持ってStringおり&str、C ++の用語で考えることは非常に便利だと思いました。

  • Stringはのようなものstd::stringです。それはメモリを所有し、メモリを管理するという汚い仕事をします。
  • Rust &strchar*(しかし少し洗練された)のようなものです。これは、の内容へのポインタを取得できるのと同じ方法で、チャンクの先頭を示しますstd::string

どちらかが消えるのでしょうか?そうは思わない。彼らは2つの目的を果たします:

Stringバッファを保持し、使用することは非常に実用的です。&str軽量で、文字列を「調べる」ために使用する必要があります。新しいメモリを割り当てる必要なく、チャンクを検索、分割、解析、さらには置き換えることができます。

&strString文字列リテラルを指すことができるので、の内部を見ることができます。次のコードは、リテラル文字列をStringマネージメモリにコピーする必要があります。

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

次のコードでは、コピーなしでリテラル自体を使用できます(ただし、読み取り専用です)。

let a: &str = "hello rust";

13
string_view?
Abhinav Gauniyal

2
はい、string_viewと同様ですが、言語に固有であり、適切にチェックを借ります。
ロッカ

41

strは、としてのみ使用され&str、文字列スライスであり、UTF-8バイト配列への参照です。

String以前~strは、拡張可能で所有されているUTF-8バイト配列でした。


技術的には、~strBox<str>
以前

3
@ jv110:いいえ、~str成長可能でBox<str>はないのに成長可能でした。(これ~str~[T]魔法の成長可能だった、他とは違っ~-object、正確な理由だったStringVec<T>規則はすべて、簡単かつ一貫していたように、導入されました。)
クリス・モーガン

18

それらは実際には完全に異なります。まず、a strは型レベルのものにすぎません。いわゆる動的サイズ型(DST)であるため、型レベルでしか推論できません。サイズstr取りは、コンパイル時に知られており、実行時の情報に依存することはできません-それは、変数に格納することはできませんコンパイラは、各変数のサイズが何であるかをコンパイル時に知っておく必要があるため。A strは概念的には単なるu8バイトの行であり、有効なUTF-8を形成することが保証されています。行の大きさはどれくらいですか?実行時まで誰も知らないため、変数に格納することはできません。

おもしろいことは、実行時にa &strや他のstrlike へのポインタBox<str> 存在することです。これは、いわゆる「ファットポインタ」です。これは、追加情報(この場合は指しているもののサイズ)を含むポインターなので、2倍の大きさになります。実際、a &strはa に非常に近くなっていますString(ただしaには近くありません&String)。A &strは2つの単語です。aの最初のバイトへの1つのポインタと、theのバイトstr数を示す別の数値str

言われていることに反して、a strは不変である必要はありません。を&mut strへの排他的ポインタとして取得できる場合、それを変更できます。変更するstrすべての安全な関数は、UTF-8制約が守られていることを保証します。違反している場合、ライブラリはこの制約がtrueであり、チェックしません。

だから何Stringですか?それは3つの単語です。2つはforと同じです&strstr、ヒープ上のバッファの容量である3番目のワードが追加されます。ヒープは常にヒープ上にあり(a strはヒープ上にあるとは限りません)、いっぱいになる前に管理され、再割り当てする必要があります。彼らが言うように、String基本的にはを所有strています。それはそれを制御し、サイズを変更し、適切と思われるときに再割り当てすることができます。つまり、a Stringはに近い&strということstrです。

もう1つはBox<str>です。これはまた、所有しているstrと、そのランタイム表現は同じである&strが、それはまた、所有しているstrとは異なり&strますが、それはそれのサイズを変更することはできません、それはその能力を知らないので、とても基本的にBox<str>、固定長として見ることができますStringすることができます(サイズを変更することはできませんStringサイズを変更する場合は、常にに変換してください)。

UTF-8制約がなく[T]、非常に類似した関係が存在しVec<T>ます。これには、サイズが動的でない任意のタイプを保持できます。

str型レベルでのの使用は、主にで汎用的な抽象化を作成すること&strです。特性を簡単に記述できるように、型レベルに存在します。理論的にstrは、型として存在する必要はありませんでしたが、それだけ&strでは、汎用になり得る多くの追加コードを記述する必要があります。

&strStringコピーせずにaの複数の異なる部分文字列を持つことができるので非常に便利です。前述のように、a String strそれが管理するヒープ上でを所有し、aの部分文字列をString新規Stringでのみ作成できる場合は、コピーする必要があります。たとえば、文字列をスライスできます。

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

str同じ文字列の2つの異なる部分文字列があります。ヒープ上stringの実際のフルstrバッファーを所有するものであり、&str部分文字列はヒープ上のそのバッファーへの単なるファットポインターです。


4

std::Stringは単にのベクトルですu8ソースコードでその定義を見つけることができます。ヒープに割り当てられ、拡張可能です。

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

strプリミティブ型であり、string 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
  • Rustの&str ===(不変)文字列

&strは、文字列のビューとして考えるのが好きです。たとえば、Java / C#のインターンされた文字列は変更できず、新しい文字列を作成するだけです。


1
Java / C#文字列とRust文字列の最大の違いは、Rustが文字列の正しいUnicodeであることを保証することです。そのため、文字列の3番目の文字を取得するには、単なる "abc" [2]よりも多くの考慮が必要です。(多言語の世界に住んでいるとすれば、これは良いことです。)
リス

これは誤りです。可変性のトピックは、すでにトップ投票で回答されています。詳細については、こちらをお読みください。
Shepmaster

-5

ここにすばやく簡単な説明があります。

String-拡張可能で所有可能なヒープ割り当てデータ構造。強制的に&str

str-(現在、Rustの進化に伴い)ヒープ上またはバイナリ内にある可変の固定長文字列です。strなどの文字列スライスビューを介して、借りた型としてのみ対話でき&strます。

使用上の考慮事項:

好むStringあなたは、文字列を所有しているか、または変異させたい場合-など、別のスレッドに文字列を渡すと

&str文字列の読み取り専用ビューが必要な場合に優先します。


これは誤りです。可変性のトピックは、すでにトップ投票で回答されています。詳細については、こちらをお読みください。
Shepmaster
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.