Rust:推測の例に慣れる

小さなプロジェクトに取り組んでRustを知ろう! Rustの基本概念を実際の例で示します。 let



match



、メソッド、関連関数、サードパーティライブラリの接続などについて学びます。 ゲームを推測するという古典的なタスクを実現しています。







  1. プログラムは1から100までの乱数を生成します。
  2. その後、彼はプレイヤーに推測を入力するように頼みます。
  3. その後、プログラムはプレーヤーに通知します。

    • 彼が数字を推測したら、ゲームは終了します
    • そうでない場合:

      • 書き込み、推測された数よりも少ないか、推測された数より多い。
      • ステップ2に進みます。


新しいプロジェクトを作成する



新しいプロジェクトを作成するには:







 $ cargo new guessing_game --bin $ cd guessing_game
      
      





最初のcargo new



コマンドは、プログラムの名前( guessing_game



)を最初の引数として受け取ります。 --bin



は、(ライブラリではなく)プログラムを作成するためのプロジェクトを準備するようCargoに指示します。 プロジェクト構成ファイル-Cargo.tomlにあるものを見てみましょう







 [package] name = "guessing_game" version = "0.1.0" authors = ["Your Name <you@example.com>"] [dependencies]
      
      





Cargoがシステムから受け取った作成者情報が正しくない場合は、構成ファイルを修正して保存します。 デフォルトでは、新しいプロジェクトには「Hello、world」が含まれています。

src / main.rs







 fn main() { println!("Hello, world!"); }
      
      





プログラムをビルドして実行しましょう:







 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs Running `target/debug/guessing_game` Hello, world!
      
      





run



コマンドは、コンパイル、ビルド、起動の各フェーズを順番に繰り返すことが多い場合に非常に役立ちます。







推測処理



プログラムの最初の部分では、ユーザーに推測を入力し、入力を処理して、正しいことを確認します。 まず、ユーザーが推測を入力できるようにします。以降、すべての変更はsrc/main.rs



に対して行われsrc/main.rs









 //        stdout use std::io; fn main() { println!("Guess the number!"); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); }
      
      





コードには多くの情報が含まれているため、徐々に検討してください。 入力された推測をユーザーから受け取り、stdoutで推測するには、 io



ライブラリをグローバルスコープに追加する必要がありますio



は、標準ライブラリ(以下std



ます)の一部です。







 use std::io;
      
      





デフォルトでは、Rustはプログラムスコープにいくつかのタイプのみをもたらします( prelude



)。 prelude



必要なタイプがない場合は、 use



ステートメントを記述してスコープに手動で追加する必要があります。 std::io



を使用すると、ユーザー入力を読み取る機能など、入出力( IO )を操作するための多くの機能が提供されます。 main



関数(CおよびC ++など)は、プログラムへのエントリポイントです。







 fn main() {
      
      





fn



使用すると、新しい関数を宣言できます。 ()



は、関数がパラメーターを受け入れないことを示し、 {



は関数の本体の前にあります。 println!



-コンソールに文字列を表示するマクロ( macro



):







 println!("Guess the number!"); //   . println!("Please input your guess.");
      
      





変数に値を保存します



次に、ユーザー入力が保存される場所を作成します。







 let mut guess = String::new();
      
      





変数を作成するために必要なlet



ステートメントに注意してください。 それは可能です。







 let foo = bar;
      
      





コードはfoo



という変数を作成し、 bar



の値をバインドしbar



。 Rustでは、デフォルトで変数は不変です。 それらを可変にするには、 let



隣の変数名にmut



を追加します。







 let foo = 5; //  (immutable) let mut bar = 5; // mutable()
      
      





let mut guess



で可変のguess



変数が作成let mut guess



ことがわかりました。 =



記号の右側は、 guess



バインドする値guess



これは、新しい文字列を返す関数であるString::new



を呼び出した結果です。 String



は、成長できる文字列型(C ++のstring



およびJavaのStringBuilder



と同様)で、 UTF-8エンコードされたテキストを含みます。 ::



in ::new



は、 new



String



型の関連関数であることを示します。 関連付けられた関数は、型、この場合はString、および型の特定のインスタンスに実装されます。 C ++などの一部の言語は、このような関数の静的メソッドを呼び出します。 new



関数は、新しい空のString



インスタンスを作成します。 new



、あるタイプの新しい値を作成する関数を示すために使用されるため、多くのタイプで実装されていることがわかります。 まとめると: let mut guess = String::new();



新しい空のString



インスタンス(空の文字列)にバインドされる新しい可変変数を作成しました。 use std::io



use std::io



してIOを操作use std::io



関数を含めました。 次に、関連する関数stdin



呼び出します。







 io::stdin().read_line(&mut guess) .expect("Failed to read line");
      
      





プログラムの最初にuse std::io



を記述しなかった場合、 std::io::stdin



形式でこの関数の呼び出しを記述できます。 stdin



関数は、 標準入力の ハンドルを表す型であるstd::io::Stdin



インスタンスを返します 。 これはユーザーのキーボードです。 コードの次の部分である.read_line(&mut guess)



、受信したばかりのポインター( handle )でread_line



を呼び出して、ユーザー入力を読み取るためのstdin



を呼び出します。 また、単一の引数&mut guess



も送信します。 read_line



は、ユーザーからstdin



入ってくるものを受け取り、この入力をストリングに入れます。したがって、ストリング変数へのリンクを受け入れます。 メソッドがユーザー入力を追加してこの文字列の値を変更できるように、この文字列引数は可変である必要があります。 &



は、引数( &mut guess



)がコードの異なる部分が同じデータ領域に値をそのデータ領域から何度もコピーすることなくアクセスできるようにする参照であることを示します。 Rustの強みの1つは、リンクが使いやすいことです(メモリを損傷することはありません)。 現時点では、変数などのリンクがデフォルトで不変であることを知るだけで十分です。 したがって、 &mut guess



&guess



(不変リンクを作成)を記述し、 &mut guess



&guess



(不変リンクを作成)を記述します。







 .expect("Failed to read line");
      
      





.foo()



構文を使用してメソッドを呼び出すときは、コード行が長くなりすぎないように、新しい行にジャンプすることをお勧めします。 これにより、他のプログラマーがコードを読みやすくなります。







 io::stdin().read_line(&mut guess).expect("Failed to read line");
      
      





少し難しく読みます。







エラー完了処理( Result



使用)



read_line



読み取るだけでなく、値も返します。ここでは、タイプio::Result



値です。 Rustには、異なるライブラリモジュールにResult



と呼ばれるいくつかのタイプがあります。









 $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) warning: unused `std::result::Result` which must be used --> src/main.rs:10:5 | 10 | io::stdin().read_line(&mut guess); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(unused_must_use)] on by default
      
      





コンパイラは、返されたResult



値を使用しなかったことを通知し、プログラムがエラーの可能性を処理しなかったことを示します。 警告を抑制するには、エラー処理コードを記述する必要がありますが、プログラムがエラーで終了する方法を確認するため、 expect



を使用expect



ます。







printlnを使用した値の印刷!



 println!("You guessed: {}", guess); //  ,   
      
      





{}



は「プレースホルダー」であり、受信した行にこの可変プレースホルダーに対応する値があることを示します。 複数の値を印刷するには、複数のプレースホルダーを使用します。最初のプレースホルダー( {}



)は最初の出力値に一致し、2番目から2番目に出力されます。 たとえば、次のように:







 let x = 5; let y = 10; println!("x = {} and y = {}", x, y); // : // x = 5 and y = 10
      
      





最初の部分の検証



 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs Running `target/debug/guessing_game` Guess the number! Please input your guess. 6 You guessed: 6
      
      





プログラムは番号を要求し、私たちが入力し、プログラムはこの番号を印刷しました。 どうぞ







秘密番号の生成



ユーザーが推測しようとする数字(1〜100)を考えてください。 これは、ゲームを複数回プレイできるように、乱数でなければなりません。 Rustにはstd



(標準ライブラリ)の乱数を扱うためのモジュールがないため、サードパーティのライブラリ(Rustに関してはcrate) rand



ます。







追加機能にはラックを使用しています。



クレートは、Rustコードの単なるパッケージです。 実行可能バイナリファイル形式の通常プログラムである実行可能クレートを作成しています。 rand



-Cの.oおよび.soファイルに似たライブラリクレート -他のクレートに接続できる関数が含まれています。 貨物は、サードパーティ製のラックの設置に非常に便利です。 rand



クレートの使用を開始するには、 Cargo.tomlに適切な変更を加えて、 rand



を依存関係として含める必要があります。 [依存関係]セクションを変更します







 [dependencies] rand = "0.3.14"
      
      





Cargo.tomlファイルは、セクションヘッダーに続くすべてが、新しいセクションが始まるまで続きます。 [dependencies]セクションは、 貨物レートを指定する場所であり、プロジェクトのコードが依存する場所、および必要なバージョンを指定します。 この場合、プロジェクトをビルドするには、バージョン0.3.14



指定子を持つrand



0.3.14



が必要であることを示し0.3.14



Cargoは、異なるバージョンのプログラムに名前を付けるための標準の1つであるセマンティックバージョニングモデルを理解しています。 0.3.14



という数値(実際、これは^0.3.14



短縮形です)は、バージョン0.3.14と互換性のあるオープン( パブリック )APIを備えたバージョンが適していることを意味します。 収集しましょう、ただし新しい依存関係があります:







 $ cargo build Updating registry `https://github.com/rust-lang/crates.io-index` Downloading rand v0.3.14 Downloading libc v0.2.14 Compiling libc v0.2.14 Compiling rand v0.3.14 Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
      
      





Cargoはリポジトリ( レジストリ-Crates.ioから依存関係をダウンロードします。 Crates.ioは、Rust開発者がオープンソースプロジェクトを投稿する場所です。 Cargo.tomlは、インデックスをダウンロードした後、まだダウンロードされていない依存関係があるかどうかを確認します。 ある場合は、それらをダウンロードします。 さらに、 rand



は彼に依存しているため、彼はまだlibc



ダウンロードしています。 これは、推移的な依存関係が自動的に解決されることを意味します。 依存関係をダウンロードした後、rustc(Rustコンパイラー)はそれらをコンパイルし、プリコンパイルされた依存関係を使用してプログラム自体をコンパイルします。 再びcargo build



を実行cargo build



と、メッセージは表示されません。 Cargoは、必要な依存関係が既にダウンロードおよびコンパイルされていることを知っており、 Cargo.tomlの [dependencies]セクションを変更していないことを知っています。 Cargoは、コードを変更していないことも知っているため、コードを再コンパイルすることはありません。 完了する必要のある作業がないため、貨物は終了します。 src / main.rsを開いて変更すると、 cargo build



はプロジェクトのコードを再度コンパイルしますが、依存関係は変更されていないため、コンパイルしません( インクリメンタルコンパイル )。







Cargo.lockは、 再現可能なビルドプログラムのビルドを支援します



Cargoには、更新可能なビルドを取得できるツールがあり、他のバージョンの依存関係を指定するまで、指定した依存関係のバージョンのみを使用します。 重要なバグ修正とコードを壊すリグレッションを含むrand



クレートバージョンv0.3.15



たらどうなりますか? この問題を解決するために、 Cargo.lockファイルが使用されます。このファイルは、初めてcagoビルドが起動されたときに作成され、現在はプロジェクトのルートディレクトリにあります。 プロジェクトを初めてアセンブルするとき、Cargoは、指定された要件( Cargo.toml内 )に一致する依存関係番号を見つけ、それらに関する情報をCargo.lockに書き込みます。 2回目以降にプロジェクトをアセンブルすると、CargoはCargo.lockがすでに存在することを確認し、そこに示されているバージョンを使用し、それらを再度表示せず、依存関係の分析に時間を費やします。 これにより、再現可能なアセンブリを取得できます。 つまり、明示的にアップグレードするまで、プロジェクトはrand



0.3.14



バージョン0.3.14



を使用します。







ラックを最新バージョンに更新する



クレートを更新する場合、Cargoはupdateコマンドを提供します。









 $ cargo update Updating registry `https://github.com/rust-lang/crates.io-index` Updating rand v0.3.14 -> v0.3.15
      
      





その後、 Cargo.lockrand



のバージョンが0.3.15に変更されたことに0.3.15



rand



バージョン0.4.0



または他のバージョン0.4.x



を使用する場合は、 Cargo.tomlを次のように更新する必要があります。







 [dependencies] rand = "0.4.0"
      
      





次回、 cargo build



を実行すると、Cargoは使用可能なクレートのインデックスを更新し、依存関係を再確認します。 Cargoを使用すると、サードパーティのライブラリを簡単に接続でき、 コードの再利用に役立ちます。 かんたん

既存のものをビルディングブロックとして使用して、新しいクレートを作成します。







乱数生成



rand



使用に移りましょう。このため、 src / main.rsを更新します







 extern crate rand; use std::io; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); }
      
      





extern crate rand;



を追加しましたextern crate rand;



、サードパーティのライブラリを接続する必要があることをrustcに示します。 rand::



記述することでrand::



から関数を呼び出す機会があるため、 use rand



use rand



ことにも似ていuse rand



use rand::Rng



も追加しました。 Rng



は、乱数ジェネレーターによって実装されるメソッドを定義する特性です。 メソッドを使用できるように、この特性はスコープ内になければなりません。 また、中央に2行追加しました。 rand::thread_rng



関数rand::thread_rng



、オペレーティングシステムによって事前に初期化( シード )されている、現在のスレッドに対してローカルな乱数ジェネレーターを返します。 次に、ジェネレーターでgen_range



を呼び出します。 このメソッドは、以前にuse rand::Rng



演算子をuse rand::Rng



してスコープに導入したRng



で定義されています。 gen_range



は2つの数値を取り、それらの間にある乱数を返します。 範囲には下限が含まれ、上限は含まれません。そのため、1〜100の数字を取得するには、数字1



101



指定する必要があります。クレートのすべての機能を理解するには、そのドキュメントを読む必要があります。 Cargoのもう1つの可能性は、 cargo doc --open



呼び出してドキュメントを「収集」できることです。その後、ブラウザでドキュメントを開きます(アセンブリ後)。 他のrand



サブ機能に興味がある場合は、左ペインでrand



アイテムを選択します。 追加した2行目は、秘密の番号を出力します。 今のところ、このままにしておきましょう。プログラムの動作を確認するのに便利です。プログラムの最終バージョンにはありません。 数回実行します。







 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs Running `target/debug/guessing_game` Guess the number! The secret number is: 7 Please input your guess. 4 You guessed: 4 $ cargo run Running `target/debug/guessing_game` Guess the number! The secret number is: 83 Please input your guess. 5 You guessed: 5
      
      





プログラムは毎回1から100までの異なる乱数を出力する必要があります。







推測を暗証番号と比較する



乱数を生成し、ユーザーから推測を得た後、それらを比較できます。 src / main.rsを変更します







 extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } }
      
      





ここで最初のなじみのない要素は、useステートメントの別の使用法です。これは、 std::cmp::Ordering



stdから scopeに変換します。 Ordering



は別の列挙であり、 Result



に似ていますが、他のオプションがあります。









 match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), }
      
      





cmp



メソッドは2つの数値を比較し、比較可能な2つのエンティティに関連して呼び出すことができます。 この要素と比較したいものへのリンクを取得します。この場合、 guess



secret_number



ます。 cmp



Ordering



オプションを返します( use



ステートメントを使用して以前にスコープに持ってきました)。 また、 match



一致を使用して、比較の結果に応じて、次に何をするかを決定します。 マッチmatch



はブランチ( arm



)で構成されます。 ブランチは、一致した式がブランチのパターンと一致する場合に実行する必要があるパターンとコードで構成されます。 Rustは、 match



ブランチ内のパターンを使用して式を順番に一致させ、 match



が見つかった後、 match



したパターンの右側のコードが実行されます。 プログラムとの可能な相互作用の例を見てみましょう。 ユーザーが推測として数値50を提案し、秘密(想定)数値が38であるとしましょう。コードが50と38を比較すると、 cmp



メソッドは50> 38であるためOrdering::Greater



返します。 。 match



は最初のブランチOrdering::Less



のテンプレートを調べますが、 Ordering::Greater



値はOrdering::Less



と比較できないため(この場合は等しくないため)、このブランチのコードを無視し、次のテンプレートとの比較を続行します。 次のブランチのテンプレートであるOrdering::Greater



Ordering::Greater



match



を渡しmatch



Ordering::Greater



同等です。 Too big!



match



, . , :







 $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) error[E0308]: mismatched types --> src/main.rs:23:21 | 23 | match guess.cmp(&secret_number) { | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable | = note: expected type `&std::string::String` = note: found type `&{integer}` error: aborting due to previous error Could not compile `guessing_game`.
      
      





, . Rust — , , , . let guess = String::new()



, Rust , guess



String



, . , secret_number



— . 1 100. :









 extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = guess.trim().parse() .expect("Please type a number!"); println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } }
      
      





:







 let guess: u32 = guess.trim().parse() .expect("Please type a number!");
      
      





guess



. , ? , Rust ( shadow ), . , . ( shadowing ) ( identifier ), , guess_str



guess



. ( bind



) guess



guess.trim().parse()



. guess



guess



, . trim



String



. u32



, ENTER



, ( read_line



). ENTER



, . , 5 ENTER



, guess



5\n



. \n



— , ENTER



. trim



\n



, 5



. parse



String



, . , Rust , let guess: u32



. ( :



) guess



, Rust , ( annotate ) . Rust , , u32



, 32- . . u32



guess



guess



secret_number



, Rust secret_number



u32



. ( u32



). parse



. , , A%



, . - , , parse



Result



, , read_line



. Result



: expect



. parse



Err



- , , expect



, expect



. parse



, parse



Ok



, expect



Ok



. :







 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs Running `target/guessing_game` Guess the number! The secret number is: 58 Please input your guess. 76 You guessed: 76 Too big!
      
      





いいね! , , 76. , - :











loop



. :







 extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); loop { println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = guess.trim().parse() .expect("Please type a number!"); println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } } }
      
      





, . :







 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Running `target/guessing_game` Guess the number! The secret number is: 59 Please input your guess. 45 You guessed: 45 Too small! Please input your guess. 60 You guessed: 60 Too big! Please input your guess. 59 You guessed: 59 You win! Please input your guess. quit thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785 note: Run with `RUST_BACKTRACE=1` for a backtrace. error: Process didn't exit successfully: `target/debug/guess` (exit code: 101)
      
      





quit



, , - . : , , .









, break



:







 extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); loop { println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = guess.trim().parse() .expect("Please type a number!"); println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } } } }
      
      





break



You win!



, , , . , .









, , . , :







 let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, };
      
      





expect



match



. , . parse



, Ok



match



, , Ok



( num



), parse



Ok



. guess



. parse



, Err



, . Err



Ok(num)



match



, Err(_)



. _



, , Err(_)



Err



. match



continue



, . :







 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Running `target/guessing_game` Guess the number! The secret number is: 61 Please input your guess. 10 You guessed: 10 Too small! Please input your guess. 99 You guessed: 99 Too big! Please input your guess. foo Please input your guess. 61 You guessed: 61 You win!
      
      





素晴らしい。 .







 extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); loop { println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } } } }
      
      





おわりに



Rust:










All Articles