Rust:パターンの&とref

続き



ご存知のとおり、Rastはパターンマッチングを実装する言語の1つです。 この用語がよくわからない場合は、値だけでなく構造によってオブジェクトを比較する一般的なスイッチ式と考えることができます。



match hashmap.get(&key) { Some(value) => do_something_with(value), None => { panic!("Oh noes!"); }, }
      
      





もちろん、比較は比較に限定されません。 上記の例でわかるように、オブジェクトは一致中に破棄されることもあり( Some(value) )、その部分は他の変数( value )に割り当てられ、 一致式の対応するブランチでさらに使用できます。



エレガントですね。 Rastaでは、パターンマッチングは、 matchだけでなく、(( iflet、さらには通常の関数引数でも)パンとバターです。



しかし、私は長い間、比較にリンクと借用を追加するとどうなるかについて漠然と考えていました。 およびrefは、これでよく使用される2つの「演算子」です。 それらの違いは、この記事に専念します。



リンクとリンク付きのタイプを作成するために使用される最初の「ステートメント」におそらく精通しているでしょう。 refは、リンクへのリンクを非常に透過的にすることも示唆しています。 ただし、これら2つの式は、照合順序内で使用される場合、 非常に異なる役割を果たします。



混乱を追加するために、それらはしばしば十分に一緒に使用されます:



 use hyper::Url; //     URL  let url = Url::parse(some_url).unwrap(); let query_params: Vec<(String, String)> = url.query_pairs().unwrap_or(vec![]); for &(ref name, ref value) in &query_params { println!("{}={}", name, value); }
      
      





それらのいずれかが存在しないことは、この式またはその式を追加する場所の提案とともに、コンパイラによって最大で注意が払われますが、もちろん常にそうなるとは限りません。 したがって、ここで実際に何が起こっているのかを理解することは非常に望ましいことです。



リンクの一部とマッピングの一部



Rastは、比較に含めることができる値に関して非常に柔軟です。 一致表現の一部になれないものを見つける努力をする必要があります。 オブジェクトとそれらへの参照の両方を問題なく使用できます。



 struct Foo(i32); // ... let foo = &Foo(42); match foo { x => println!("Matched!"), }
      
      





ただし、前の例では、リンク自体は通常、私たちにとって興味深いものではありませんが、どこを指すのかはわかりません。



 match foo { &Foo(num) => println!("Matched with number {}", num), }
      
      





ご覧のとおり、これにはアンパサンドが使用されています。 タイプ( SomeOk、または前述のFoo )と同様に、 は一致するときに期待する値を示します。 アンパサンドを見ると、プログラムは、オブジェクト自体ではなく、特定のオブジェクトへの参照をマップすることを知っています。



しかし、なぜオブジェクトとオブジェクトへの参照のこの区別がそれほど重要なのでしょうか? 他の場所では、Rastは、オブジェクトのメソッドを呼び出す場合など、リンクとオブジェクト自体の間の境界線をぼかすのに十分な柔軟性を備えています。 (このためのメカニズムは、「 参照解除変換 」または英語では「 参照解除強制」と呼ばれます)



ただし、値をコンポーネント部分に展開する可能性があるため、サンプルとの比較は破壊的な操作です。 一致 (または別の同様の式)を適用するすべては、デフォルトでこのブロックに移動されます:



 let maybe_name = Some(String::from("Alice")); // ... match maybe_name { Some(n) => println!("Hello, {}", n), _ => {}, } do_something_with(maybe_name)
      
      





所有権のセマンティクスと完全に一致して、 一致式は、その後の移動の試みを防ぎ、本質的にオブジェクトを吸収します。 したがって、上記のコードは次のエラーメッセージを生成します。



 error: use of partially moved value: `maybe_name` [E0382] do_something_with(maybe_name); ^~~~~~~~~~
      
      





したがって、 Someのように、アンパサンドは基本的に、マッピングするパターンの一部にすぎません。 また、 Someや他の型と同様に、明らかな対称性があります。値を作成するためにを使用した場合、アンパサンドを使用して展開する必要があります。



オブジェクトを解凍するためのパターンで使用される構文は、オブジェクトの作成に使用される構文と似ています。




動きを防ぐ



上記のようなエラーには、多くの場合、有用な注意事項が含まれています。



 note: `(maybe_name:core::option::Option::Some).0` moved here because it has type `collections::string::String`, which is moved by default Some(n) => println!("Hello, {}", n), ^
      
      





そして、潜在的にエラーを修正する方法:



 help: if you would like to borrow the value instead, use a `ref` binding as shown: Some(ref n) => println!("Hello, {}", n),
      
      





これは、 refがシーンに登場する場所です。



このメッセージは、提案された場所にrefキーワードを追加すると、このマッピングで使用される変数の借用への移動を変更することを示しいます。 (この場合、これはnです )前と同様に、この変数は必要な値をキャプチャしますが、今回それを所有していません。



そして、この違いは非常に重要です。



アンパサンドとは異なり、 refは比較対象を意味しません。 値がこのパターンに一致するかどうかにはまったく影響しません。



変更されるのは、一致した値の一部がパターン変数によってどのようにキャプチャされるかだけです。





この例では、パターンSome(n)の変数nString型です。 マップされた構造と同じタイプ。 対照的に、 Some(ref n)パターンの他のn&String型、つまり オブジェクトフィールドへのリンク。



1つ目は移動、2つ目は借用です。



refキーワードは、一致するパターン内の変数が値を移動するのではなく借用することを意味します。 これは、直接比較の観点からはパターンの一部ではありません


一緒に使用する



それでは、記事の冒頭からその紛らわしい例で正確に何が起こるかを調べてみましょう。



 for &(ref name, ref value) in &query_params { println!("{}={}", name, value); }
      
      





なぜなら refはパターンが一致するかどうかに影響ないことを知っています。 &(a、b)のようなものを挿入するだけで、このオプションはより読みやすくなります。 。 もちろん、反復が発生するベクトルのメンバーであるのは、まさにそのようなタプルです。



問題は、参照なしではタプル値をループに移動しようとしているが、 &query_paramsを反復処理し、各タプルのみを借用するため、このような移動は実際には不可能です。 基本的に、これは借用コンテキストから値を移動する古典的な試みです。



しかし、この運動は必要ありません。 ループが行うのは値を出力することだけなので、リンクを介して値にアクセスするだけで十分です。



そして、これはまさにrefが私たちに与えるものです。 このキーワードを返したら、移動から借用に移行します。



まとめ






All Articles