Rust関数の文字列と&str


この投稿は、プログラムをコンパイルするために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


参照解除キャストの例


この例では、行は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(); //  String::from_str(", ") print_me(&owned_string); let counted_string = std::rc::Rc::new(", ".to_string()); print_me(&counted_string); let atomically_counted_string = std::sync::Arc::new(", ".to_string()); print_me(&atomically_counted_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は、 Personname参照を生き残ることができないことを確認しようとします。 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); // move error } 

ここでは、変数を構造体に配置する前に変数を使用する必要があるため、リンクを使用する必要があります。 実際の例はrustc_serializeです。 Encoder構造は、 std::fmt::Writeを実装するwriter変数を所有する必要がないため、借用のみが使用されます。 実際、 StringWrite実装しています。 この例では、 encode関数を使用encodeと、 String型の変数がEncoder渡され、 encode返されます。


これで、 &strString形式の文字列を受け入れる関数を作成できます。さらに、参照カウンターを使用することもできます。 リンクを含む構造を作成することもできます。 構造体の寿命は、その構造体に含まれるリンクに関連付けられているため、構造体は、それが参照する変数に耐えることができないため、プログラムでエラーが発生します。 これで、構造内でリンクを使用する場合と使用しない場合の基本的な理解が得られました。

「静的」について


もう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プログラムの寿命を通して有効です。 あなたはPersonnameそんなに長く生きることname望みません。 (たとえば、プログラム自体にコンパイルされた静的文字列リテラルは、タイプ&'static str 。つまり、プログラムの存続期間中-およそTransl。)

他に読むもの


Source: https://habr.com/ru/post/J274485/


All Articles