
この投稿は、プログラムをコンパイルするために
to_string()を使用する必要性に混乱しているすべての人々に捧げられています。 そして、Rustが
Stringと
&str 2つの文字列型を持っている理由の問題に光を当てたいと思い
&str 。
文字列を受け入れる関数
文字列を受け入れるインターフェイスを作成する方法について説明します。 私はハイパーメディアの大ファンであり、ユーザーフレンドリーなインターフェースの作成に情熱を注いでいます。
Stringを取るメソッドから始めましょう。 この検索により、
std::string::String型が得られますが、これは最初は悪くありません。
fn print_me(msg: String) { println!(": {}", msg); } fn main() { let message = ", "; print_me(message); }
コンパイルエラーが発生します。
expected `collections::string::String`, found `&'static str`
&strような文字列リテラル
&str String互換性
&strないことが
&str 。 コンパイルが成功するように、
message変数の型を
Stringに変更する必要があり
message let message = ", ".to_string(); 。 動作しますが、
clone()を使用して所有権継承エラーを修正するようなものです。
print_me引数
print_meを
&strに変更する3つの理由を次に
print_me &str 。
&記号は参照タイプを示します。 つまり、変数を貸し出します。 print_meが変数のprint_meすると、所有権は元の所有者に戻ります。 message変数の所有権を関数に転送する正当な理由がない限り、借入を使用する必要があります。- リンクを使用する方が効率的です。
message Stringを使用すると、プログラムは値をコピーする必要があります。 &strなどのリンクを使用する場合、コピーは発生しません。 String型は、 Derefと型キャストを使用して魔法のように&strに変換でき&str 。 例は、この点をはるかに改善します。
参照解除キャストの例
この例では、行は4つの異なる方法で作成され、それらはすべて
print_me関数で機能します。 このすべてが機能する主な点は、参照による値の転送です。
owned_string自身の文字列を
Stringとして渡す代わりに、
&Stringポインタとして渡します。 コンパイラは、
&String &strが
&strを取る関数に
&String渡されることを認識すると、
&Stringを
&strにキャストし
&str 。 通常のアトミックリンクカウンターで文字列を使用する場合、まったく同じ変換が使用されます。
string変数はすでに参照されているため、
print_me(string)呼び出すときに
&を使用する必要はありません。 この知識があれば、コードで
.to_string()を常に呼び出す必要はなくなりました。
fn print_me(msg: &str) { println!("msg = {}", msg); } fn main() { let string = ", "; print_me(string); let owned_string = ", ".to_string();
また、
Vecベクトルなどの他のタイプで逆参照キャストを使用することもできます。 それでも、
Stringは8バイト文字の単なるベクトルです。
強制参照の詳細については、「
Rust Programming Language 」(
英語 )
を参照して
ください 。
構造の使用
この時点で、不必要な
to_string()呼び出しはすでにないはずです。 ただし、構造体を使用すると問題が発生する場合があります。 利用可能な知識を使用して、次の構造を作成できます。
struct Person { name: &str, } fn main() { let _person = Person { name: "Herman" }; }
次のエラーが表示されます。
<anon>:2:11: 2:15 error: missing lifetime specifier [E0106] <anon>:2 name: &str,
Rustは、
Personが
name参照を生き残ることができないことを確認しようとします。
Person name生き延びた場合、プログラムがクラッシュするリスクがあります。 Rustの主な目標は、これを防ぐことです。 このコードをコンパイルしてみましょう。 Rustがセキュリティを提供できるように、
ライフタイム (
英語 )またはスコープを指定する必要があります。 通常、ライフタイムは
'aと呼ばれます。 そのような伝統がどこから来たのかはわかりませんが、私たちはそれに従います。
struct Person { name: &'a str,' } fn main() { let _person = Person { name: "Herman" }; }
コンパイルしようとすると、次のエラーが表示されます。
<anon>:2:12: 2:14 error: use of undeclared lifetime name `'a` [E0261] <anon>:2 name: &'a str,
考え直してみましょう。
Person構造は
nameフィールドを超えてはいけないという考えをRustコンパイラに何らかの形で伝えたいことがわかっています。 したがって、
Person構造体の有効期間を宣言する必要があります。 短い検索により、ライフタイムを宣言するための構文
<'a>導かれます。
struct Person<'a> { name: &'a str, } fn main() { let _person = Person { name: "Herman" }; }
コンパイルします! 通常、構造体にいくつかのメソッドを実装します。
greetメソッドを
Personクラスに追加しましょう。
struct Person<'a> { name: &'a str, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
次のエラーが表示されます。
<anon>:5:6: 5:12 error: wrong number of lifetime parameters: expected 1, found 0 [E0107] <anon>:5 impl Person {
Person構造には有効期間パラメーターがあるため、実装にもそれが必要です。 次のように
Person実装でライフタイムを宣言しましょう:
impl Person<'a> { 。 残念ながら、今ではこのような奇妙なコンパイルエラーが発生します。
<anon>:5:13: 5:15 error: use of undeclared lifetime name `'a` [E0261] <anon>:5 impl Person<'a> {
ライフタイムを
宣言するには 、次のように
impl直後にライフタイムを指定する必要があります
impl<'a> Person { 。 もう一度コンパイルすると、エラーが発生します。
<anon>:5:10: 5:16 error: wrong number of lifetime parameters: expected 1, found 0 [E0107] <anon>:5 impl<'a> Person {
すでにより明確です。 このような実装の
Person構造の説明に有効期間パラメーターを追加しましょう
impl<'a> Person<'a> { 。 これでプログラムがコンパイルされます。 完全な作業コードは次のとおりです。
struct Person<'a> { name: &'a str, } impl<'a> Person<'a> { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
構造体の文字列または&str
ここでの質問は、いつ
Stringを使用する価値があるのか、および構造体の
&strするのかということです。 つまり、構造内の別の型への参照をいつ使用する必要がありますか? 構造が変数の所有権を必要としない場合は、リンクを使用する必要があります。 意味が少しぼやけている場合があるため、この質問に答えるためにいくつかのルールを使用します。
- 構造外で変数を使用する必要がありますか? 少し工夫された例を次に示します。
struct Person { name: String, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let name = String::from_str("Herman"); let person = Person { name: name }; person.greet(); println!(" {}", name);
ここでは、変数を構造体に配置する前に変数を使用する必要があるため、リンクを使用する必要があります。 実際の例は
rustc_serializeです。
Encoder構造は、
std::fmt::Writeを実装する
writer変数を所有する必要がないため、借用のみが使用されます。 実際、
Stringは
Write実装しています。 この例では、
encode関数を使用
encodeと、
String型の変数が
Encoder渡され、
encode返されます。
- 私のタイプは大きいですか? タイプが大きい場合、参照による転送はメモリを節約します。 参照渡しでは変数がコピーされないことに注意してください。 大量のデータを持つ
String型のバッファを想像してください。 別の関数に転送されるたびにコピーすると、プログラムの速度が大幅に低下する可能性があります。
これで、
&str 、
String形式の文字列を受け入れる関数を作成できます。さらに、参照カウンターを使用することもできます。 リンクを含む構造を作成することもできます。 構造体の寿命は、その構造体に含まれるリンクに関連付けられているため、構造体は、それが参照する変数に耐えることができないため、プログラムでエラーが発生します。 これで、構造内でリンクを使用する場合と使用しない場合の基本的な理解が得られました。
「静的」について
もう1点注意する価値があると思います。 静的ライフタイム
'static (最初の例のように)を使用して、サンプルを強制的にコンパイルできますが、これを行うことはお勧めしません。
struct Person { name: &'static str, } impl Person { fn greet(&self) { println!(", {}", self.name); } } fn main() { let person = Person { name: "Herman" }; person.greet(); }
静的寿命
'staticプログラムの寿命を通して有効です。 あなたは
Personや
nameそんなに長く生きること
name望みません。
(たとえば、プログラム自体にコンパイルされた静的文字列リテラルは、タイプ&'static str 。つまり、プログラムの存続期間中-およそTransl。)他に読むもの