Chapter 03

プログラムの書き方とエラー

対応する APG4b の箇所:1.02.プログラムの書き方とエラー

フォーマット

ソースコード中で,スペースや改行といった「ホワイトスペース」と呼ばれる文字は,区切りとして以外の意味を持ちません.どういうことかというと,

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

というコードは,余計なホワイトスペースを全て省いて

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

と書いても同じように動きます.この中で省けないのはただ一つ, fnmain を区切っているホワイトスペースだけです.

しかし,プログラムの読みやすさを考えると,このようにホワイトスペースを削ってコードを書くのはよろしくありません.フォーマットの仕方がある程度決められています.

fn main() { } は関数定義の構文ですが, fn main() を書いてから一つ空白を開けて { を書き,そのあと改行を挟んでプログラムの内容を書き始めます.最後の } は,行頭に置きます.

{} の間の部分は,行頭に 4 つ空白文字を入れてから書き始めます.すなわち,

fn main() {
    println!("Hello");
    // このコメントや上の println! より前にある 4 つの空白文字
}

のことです. {} で囲まれた部分をブロックといいますが,ブロックの中身はこうして行頭に空白文字を入れるようにしておくと,ブロックの始まりと終わりが見て分かりやすくなります.このように,空白やタブを使ってコードの一部を右にずらして書くことをインデントといいます.

この記事に出てくるコードは, rustfmt という,フォーマッタと呼ばれるツールで自動的にフォーマットされています.この記事と同じようにスペースを入れていれば大体読みやすいコードが書けます.

エラー

コンパイルエラー

次のコードを AtCoder のコードテストで実行してみましょう.

fn main() {
    println("Hello");
}

println! の最後のエクスクラメーションマーク ! を書き忘れているため,コンパイルエラーになります.これを提出すると, CE というステータスが返ってきます.

実行時エラー

実行時エラーは,プログラムの文法等は正しかったが,プログラムを実行する際に何らかの問題が生じた(0 で割り算してしまった,など)ときに起こります.次のコードをコードテストで実行してみましょう.

fn main() {
    panic!();
}

panic! マクロは,書くだけで実行時エラーを引き起こします.あえて実行時エラーにしたいときに使います.実行時エラーが起こると,コードテスト上では終了コードが 0 以外の値になります.一方,これを提出すると, RE というステータスが返ってきます.

コンパイルエラーの読み方

コンパイルエラーが起こったとき,エラーメッセージにそのエラーについての情報が書かれています.エラーメッセージを読むのは非常に大事です.

ケース 1

たとえば,さっきの間違ったコード

fn main() {
    println("Hello");
}

をコンパイルすると,

error[E0423]: expected function, found macro `println`
 --> src/main.rs:2:5
  |
2 |     println("Hello");
  |     ^^^^^^^ help: use `!` to invoke the macro: `println!`

というエラーが出力されます.

最初の expected function, found macro `println` というのは,「println(...); という構文中で println の部分には関数が来るはずだが,実際には println は関数ではなくマクロだった」という意味です.

それに続き src/main.rs:2:5 と書いているのは,これが src/main.rs というファイル中の 2 行目 5 文字目で発生したエラーであるという意味です(AtCoder ではファイル名は常に src/main.rs です).よってソースコードの 2 行目周辺に着目することでエラーの原因が判明する可能性が高いです.

さらにその後に,

  |
2 |     println("Hello");
  |     ^^^^^^^ help: use `!` to invoke the macro: `println!`

と書かれています. 2 行目の println の部分について, help: use `!` to invoke the macro: `println!`,つまり「println! マクロを呼び出したいのであれば, ! を使うのはどうか」と提案されています.この help の指示に従うことでエラーが解決することは多いです.

ケース 2

今度は次のコードをそっくりそのままコピーペーストしてコードテストを実行してみましょう.

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

見た目では非常に分かりづらいのですが, 2 行目のインデントが半角空白「 」ではなく全角空白「 」になっています.空白文字として全角空白を用いることは許されていません.これをコンパイルすると

error: unknown start of token: \u{3000}
 --> src/main.rs:2:1
  |
2 |  println!("Hello");
  | ^^
  |
help: Unicode character ' ' (Ideographic Space) looks like ' ' (Space), but it is not
  |
2 |  println!("Hello");
  | --

というエラーが出力されます.Unicode character ' ' (Ideographic Space) looks like ' ' (Space), but it is not というのは,「Unicode において Ideographic Space と名付けられている文字 ' ' は Space という文字 ' ' に似ているが,これらは異なる文字である」という意味です.よって,全角空白を半角空白に直せば良いのだと分かります.

このように,エラーが起こったらまずはエラーメッセージを読むのが大事です.