回答:
String
動的ヒープ文字列型Vec
です。たとえば、文字列データを所有または変更する必要がある場合に使用します。
str
メモリ内の動的な長さのUTF-8バイトの不変の1シーケンスです。サイズは不明なので、ポインタの後ろでしか処理できません。これは、str
最も一般的には2が次のように表示されることを意味し&str
ます。通常は「文字列スライス」または単に「スライス」と呼ばれる一部のUTF-8データへの参照。スライスは一部のデータのビューであり、そのデータはどこにでも配置できます。
"foo"
は、文字列リテラルは&'static str
です。データは実行可能ファイルにハードコードされ、プログラムの実行時にメモリに読み込まれます。String
:String
へのデリファレンス&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では、これらのタイプを簡単に作成できます。
[u8; N]
。
Rc<str>
なりましたArc<str>
。
私はC ++のバックグラウンドを持ってString
おり&str
、C ++の用語で考えることは非常に便利だと思いました。
String
はのようなものstd::string
です。それはメモリを所有し、メモリを管理するという汚い仕事をします。&str
はchar*
(しかし少し洗練された)のようなものです。これは、の内容へのポインタを取得できるのと同じ方法で、チャンクの先頭を示しますstd::string
。どちらかが消えるのでしょうか?そうは思わない。彼らは2つの目的を果たします:
String
バッファを保持し、使用することは非常に実用的です。&str
軽量で、文字列を「調べる」ために使用する必要があります。新しいメモリを割り当てる必要なく、チャンクを検索、分割、解析、さらには置き換えることができます。
&str
String
文字列リテラルを指すことができるので、の内部を見ることができます。次のコードは、リテラル文字列をString
マネージメモリにコピーする必要があります。
let a: String = "hello rust".into();
次のコードでは、コピーなしでリテラル自体を使用できます(ただし、読み取り専用です)。
let a: &str = "hello rust";
それらは実際には完全に異なります。まず、a str
は型レベルのものにすぎません。いわゆる動的サイズ型(DST)であるため、型レベルでしか推論できません。サイズstr
取りは、コンパイル時に知られており、実行時の情報に依存することはできません-それは、変数に格納することはできませんコンパイラは、各変数のサイズが何であるかをコンパイル時に知っておく必要があるため。A str
は概念的には単なるu8
バイトの行であり、有効なUTF-8を形成することが保証されています。行の大きさはどれくらいですか?実行時まで誰も知らないため、変数に格納することはできません。
おもしろいことは、実行時にa &str
や他のstr
like へのポインタ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と同じです&str
がstr
、ヒープ上のバッファの容量である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
では、汎用になり得る多くの追加コードを記述する必要があります。
&str
String
コピーせずに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
部分文字列はヒープ上のそのバッファーへの単なるファットポインターです。
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を少し変更するとバイト長が変更される可能性があり、スライスはその参照先を再割り当てできません。
簡単に言えば、String
はデータ型がヒープに格納されている(と同じようにVec
)ので、その場所にアクセスできます。
&str
スライスタイプです。つまりString
、ヒープ内のすでに存在する場所への参照にすぎません。
&str
実行時に割り当てを行いません。したがって、メモリ上の理由から、&str
over を使用できますString
。ただし、使用&str
する場合は、明示的なライフタイムを処理する必要がある場合があることに注意してください。
str
がview
すでにString
ヒープに存在しているということでした。
C#およびJavaのユーザー向け:
String
===StringBuilder
&str
===(不変)文字列私&str
は、文字列のビューとして考えるのが好きです。たとえば、Java / C#のインターンされた文字列は変更できず、新しい文字列を作成するだけです。
ここにすばやく簡単な説明があります。
String
-拡張可能で所有可能なヒープ割り当てデータ構造。強制的に&str
。
str
-(現在、Rustの進化に伴い)ヒープ上またはバイナリ内にある可変の固定長文字列です。str
などの文字列スライスビューを介して、借りた型としてのみ対話でき&str
ます。
使用上の考慮事項:
好むString
あなたは、文字列を所有しているか、または変異させたい場合-など、別のスレッドに文字列を渡すと
&str
文字列の読み取り専用ビューが必要な場合に優先します。
&str
二つの成分で構成されています。いくつかのバイトへのポインタ、および長さを。」