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は、 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); // move error }
      
      





ここでは、変数を構造体に配置する前に変数を使用する必要があるため、リンクを使用する必要があります。 実際の例はrustc_serializeです。 Encoder



構造は、 std::fmt::Write



を実装するwriter



変数を所有する必要がないため、借用のみが使用されます。 実際、 String



Write



実装しています。 この例では、 encode



関数を使用encode



と、 String



型の変数がEncoder



渡され、 encode



返されます。





これで、 &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。)




他に読むもの






All Articles