こんにちは!今日もThe Rust Programming Language 日本語版 (通称 “The Book”) を片手に、Rustの学習を進めていきます。
今回は、The Bookの3章で学んだことを活かして、簡単なプログラムを作ってみます!
具体的には、
- 変数
- 関数
- 標準入力
if
式loop
を使って、華氏と摂氏を相互に変換するプログラムを作成します。
少しずつ機能を増やしながら、段階的にプログラムを改良していく過程を、全てお見せします!
Rust初心者の方も、一緒に手を動かしながら、プログラミングの楽しさを体験してみましょう!
まずは変数と計算:華氏を摂氏に変換
まずは、変数に値を代入し、その値を使って計算 (華氏から摂氏への変換) を行うプログラムを作成します。
fn main() {
let celsius: i32 = 22;
let fahrenheit: i32 = celsius * 9 / 5 + 32;
println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
}
コード解説:
let celsius: i32 = 22;
: 摂氏の値を格納する変数celsius
を宣言し、i32
型 (32ビット整数) であることを指定しています。let fahrenheit: i32 = celsius * 9 / 5 + 32;
: 華氏の値を計算し、変数fahrenheit
に代入しています。println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
: 計算結果を出力しています。
非常にシンプルなプログラムですが、変数、型、計算、出力といった、プログラミングの基本的な要素が含まれています。
関数を使ってコードを整理
次に、温度変換の計算部分を関数に切り出して、コードを整理します。
fn main() {
let celsius: i32 = 22;
let fahrenheit: i32 = henkan(celsius);
println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
}
fn henkan(celsius: i32) -> i32 {
let fahrenheit: i32 = celsius * 9 / 5 + 32;
return fahrenheit;
}
変更点:
henkan
関数を新たに定義しました。celsius: i32
:i32
型の引数celsius
を受け取ります。-> i32
:i32
型の値を返します。- 関数本体で、華氏への変換計算を行い、
return
で結果を返します。
main
関数内で、henkan
関数を呼び出すように変更しました。
関数を使うことで、
- コードが読みやすくなる
- 同じ処理を繰り返し使う場合に便利
といったメリットがあります。
標準入力でインタラクティブに!
さらに、プログラムを実行するたびにコードを書き換えるのではなく、ユーザーに値を入力してもらうように改良します。
//use std::io;で標準入力を受け付ける機能をインポートします。
use std::io;
fn main() {
//ターミナル上に摂氏は?と表示して標準入力を受け付ける
println!("摂氏は?");
//String型の変数celsiusを宣言します。
//mutで可変変数を宣言することでこの変数の値を変更することが出来るようにします。
let mut celsius_str: String = String ::new();
//ioのstdin関数を呼び出して標準入力を受け付けます。
//read_line関数は標準入力を受け付けて変数に格納します。
//&mut celsiusはcelsiusの参照を渡します。
//expectはエラーが発生した場合にエラーメッセージを表示します。
io::stdin()
.read_line(&mut celsius_str)
.expect("Failed to read line");
//celsiusの値をi32型に変換します。
//trim関数は文字列の先頭と末尾の空白を削除します。
//parse関数は文字列を数値に変換します。
//expectはエラーが発生した場合にエラーメッセージを表示します。
let celsius: i32 = celsius_str
.trim()
.parse()
.expect("数字を入力してください。");
//henkan関数を呼び出して華氏に変換します。
let fahrenheit: i32 = henkan(celsius);
println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
}
fn henkan(celsius: i32) -> i32 {
let fahrenheit: i32 = celsius * 9 / 5 + 32;
return fahrenheit;
}
変更点:
use std::io;
で、標準入出力ライブラリをインポートしました。println!("摂氏は?");
で、ユーザーに値の入力を促します。let mut celsius_str: String = String::new();
で、可変な文字列型の変数celsius_str
を宣言します。io::stdin().read_line(&mut celsius_str).expect("Failed to read line");
で、標準入力を読み込み、celsius_str
に格納します。io::stdin()
: 標準入力 (キーボードからの入力) を表すオブジェクトを取得します。read_line(&mut celsius_str)
: 標準入力から1行読み込み、celsius_str
に格納します。&mut
は、可変な参照を渡すことを意味します。expect("Failed to read line")
: 読み込みに失敗した場合 (例えば、ファイル終端に達した場合など) に、プログラムを終了し、指定したメッセージを表示します。
let celsius: i32 = celsius_str.trim().parse().expect("数字を入力してください。");
で、celsius_str
を数値に変換し、変数celsius
に代入します。trim()
: 文字列の前後の空白文字 (スペース、タブ、改行など) を削除します。parse()
: 文字列を指定した型 (ここではi32
) に変換します。expect("数字を入力してください。")
: 変換に失敗した場合 (例えば、文字列が数字でない場合など) に、プログラムを終了し、指定したメッセージを表示します。
The Bookからの引用
let mut celsius_str = String::new();
という行は可変変数を作成し、その変数は現時点では新しい空のStringのインスタンスに束縛されているわけです。 ふう!
標準入力を受け付けるだけでもこれだけのコードが必要になるんですね…。
&mut celsius_str
で指定している参照も難しいですね。
The Bookには
この&は、この引数が参照であることを示し、これによりコードの複数の部分が同じデータにアクセスしても、そのデータを何度もメモリにコピーしなくて済みます。 参照は複雑な機能(訳注:一部のプログラム言語では正しく使うのが難しい機能)ですが、Rustの大きな利点の一つは参照を安全かつ簡単に使用できることです。 このプログラムを完成させるのに、そのような詳細を知る必要はないでしょう。 とりあえず知っておいてほしいのは、変数のように参照もデフォルトで不変であることです。 したがって、&celsius_strではなく&mut celsius_strと書いて可変にする必要があります。 (参照については第4章でより詳しく説明します)
と書かれており、今はまだ理解できなくてもいい部分らしいです。
loop
で繰り返し処理
最後に、loop
を使って、何度でも温度変換できるようにプログラムを改良します。
use std::io;
fn main() {
println!("ようこそ、このプログラムは摂氏を華氏に変換します。");
loop{
println!("摂氏は?");
let mut celsius_str: String = String ::new();
io::stdin()
.read_line(&mut celsius_str)
.expect("Failed to read line");
let celsius: i32 = celsius_str
.trim()
.parse()
.expect("数字を入力してください。");
let fahrenheit: i32 = henkan(celsius);
println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
}
}
fn henkan(celsius: i32) -> i32 {
let fahrenheit: i32 = celsius * 9 / 5 + 32;
return fahrenheit;
}
変更点:
main
関数内の処理全体をloop
で囲みました。
これで、プログラムを終了するまで (Ctrl+Cを押すまで)、何度でも温度変換を繰り返すことができます。
現状、数字以外を入力すると、expect("数字を入力してください。")
でプログラムが終了します。
摂氏・華氏の相互変換に対応!
さらに、摂氏から華氏だけでなく、華氏から摂氏への変換もできるように、プログラムを拡張してみましょう。
use std::io;
fn main() {
println!("ようこそ、このプログラムは摂氏を華氏に変換します。");
loop{
println!("摂氏ですか?華氏ですか?");
println!("摂氏ならcを、華氏ならfを入力してください。");
let mut c_or_f: String = String::new();
io::stdin()
.read_line(&mut c_or_f)
.expect("Failed to read line");
let c_or_f: char = c_or_f
.trim()
.parse()
.expect("cかfを入力してください。");
if c_or_f == 'c' {
println!("摂氏は?");
let mut celsius_str: String = String ::new();
io::stdin()
.read_line(&mut celsius_str)
.expect("Failed to read line");
let celsius: i32 = celsius_str
.trim()
.parse()
.expect("数字を入力してください。");
let fahrenheit: i32 = henkan_c(celsius);
println!("摂氏{}°Cは華氏{}°Fです。", celsius, fahrenheit);
}
else if c_or_f == 'f' {
println!("華氏は?");
let mut fahrenheit_str: String = String ::new();
io::stdin()
.read_line(&mut fahrenheit_str)
.expect("Failed to read line");
let fahrenheit: i32 = fahrenheit_str
.trim()
.parse()
.expect("数字を入力してください。");
let celsius: i32 = henkan_f(fahrenheit);
println!("華氏{}°Fは摂氏{}°Cです。", fahrenheit, celsius);
}
}
}
fn henkan_c(celsius: i32) -> i32 {
let fahrenheit: i32 = celsius * 9 / 5 + 32;
return fahrenheit;
}
fn henkan_f(fahrenheit: i32) -> i32 {
let celsius: i32 = (fahrenheit -32) * 5 / 9;
return celsius;
}
変更点:
- 最初に、摂氏 (c) か華氏 (f) かを入力してもらうようにしました。
- 入力された文字に応じて、処理を分岐 (
if
式) します。 - 摂氏→華氏の変換関数
henkan_c
と、華氏→摂氏の変換関数henkan_f
を定義しました。
今回の学習を終えて
今回は、Rustの制御フロー (if
, loop
) を使いながら、簡単な温度変換プログラムを作成しました。
The Bookで学んだことを実際にコードに落とし込むことで、理解が深まったように感じます。
今回作成したプログラムは、まだまだ改善の余地があります。
- 現状だと数字やc,f以外の文字を入力するとErrorで終了してしまうので、入力値チェックやエラー処理
- 終了処理
など、今後、機能を拡張していくのが楽しみです!
次回は、Rustの「所有権」について学習する予定です。
所有権は、Rustの最も特徴的な概念であり、理解するのが難しいとよく言われているので、気合を入れて臨みたいと思います!