Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rubyのエラーを整理(初級者向け)

 Rubyのエラーを整理(初級者向け)

Y. Sato ( ****)

October 24, 2020
Tweet

More Decks by Y. Sato ( ****)

Other Decks in Programming

Transcript

  1. エラーの分類 ⼀般なエラーの段階別の分類 構⽂エラー = シンタックスエラー(Syntax error) 実⾏時エラー = ランタイムエラー(Runtime error)

    ザックリいうと、 構⽂エラーは、⽂法・構⽂(syntax)が正しくないエラー。 実⾏時エラーは、実⾏してるときに起こったエラー。 Rubyのエラーを整理(初級者向け) 2020/10/24 5
  2. Rubyでの構⽂エラー = シンタックスエラーのエラー Rubyでの主なシンタックスエラー ブロックが、⾜りなかったり、対応できていない。 end が⾜りない。 波括弧 {} が対応できていない。

    配列やハッシュの中にカンマに過不⾜がある。 変なところに記号や予約語がある。 ハッシュをブロックと勘違いされる。 解釈できない数値がある。ex) 0o80 Rubyのエラーを整理(初級者向け) 2020/10/24 7
  3. 構⽂エラーの例: ブロックの終わりの } を書き忘れる Ruby (1..3).each { |i| puts i

    ↓ Main.rb:2: syntax error, unexpected end-of-input, expecting '}' end-of-input はわかりにくいですが、「実⾏してるファイルの終わり」の意味。 全体を訳すと「Main.rbというファイルの2⾏⽬で、シンタックスエラーです︕ 予期せ ぬファイルの終端です、 } を期待していたのに」という意味。 Rubyのエラーを整理(初級者向け) 2020/10/24 8
  4. Rubyの構⽂エラーの例: 変なところに記号や予約語がある = ↓ Main.rb:1: syntax error, unexpected '=' (訳:

    構⽂エラー、予期せぬ`=`) 変なところに記号や予約語があることによるエラーです。 記号1⽂字だけでもエラーの⽂が全然違ったりするので、試してみると⾯⽩いかもしれ ないです。例) @ , + , ' , / , % , = , : , ! Rubyのエラーを整理(初級者向け) 2020/10/24 9
  5. Rubyの構⽂エラーの例: doと|i|の位置を間違える 慣れてない頃、よくこうやって順番を間違えていた記憶 5.times |i| do end ↓ Main.rb:1: syntax

    error, unexpected `do' (訳: 構⽂エラー、予期せぬ`do`) 5.times |i| do ^~ 「unexexpected `|'」と表⽰されるべきな気がするが、「do」がでる。 エラーの箇所はわかるけど、エラー内容はちょっとわかりにくい気がする。 実際のコードはもっと複雑だろうし、もっとわかりにくい。 Rubyのエラーを整理(初級者向け) 2020/10/24 10
  6. Rubyの構⽂エラーの例: カンマが過剰 [,] ↓ Main.rb:1: syntax error, unexpected ',', expecting

    ']' 要素がないのに , が急に現れて、予期せぬ , となっている。 無駄なカンマがあるときに⾒られるエラー。 Rubyのエラーを整理(初級者向け) 2020/10/24 11
  7. Rubyの構⽂エラーの例: カンマが不⾜ p [1 2] ↓ Main.rb:1: syntax error, unexpected

    integer literal, expecting ']' p [1 2] ^ 要素の間の , が不⾜していて、予期せぬ整数(integer)があることによるエラー。 【余談】⽂字列の間に空⽩があっても、1つの⽂字列になります(何故?) "Hello, " "world!" #=> "Hello, world!" Rubyのエラーを整理(初級者向け) 2020/10/24 12
  8. Rubyの構⽂エラーの例: ⽂字列の囲い込み忘れ p 'Hello, world! ↓ Main.rb:1: unterminated string meets

    end of file p 'Hello, world! ⽂字列を囲み忘れたことによるエラー。 syntax errorだけど、エラー⽂にsyntax errorと表⽰されないパターン. エラー⽂を直訳すると「終了のない⽂字列が、ファイルの終わりに交わった」(?) Rubyのエラーを整理(初級者向け) 2020/10/24 13
  9. Rubyの構⽂エラーの例: ハッシュをブロックと勘違いされる これは、ちょっと有名なやつです。 p {"Ruby" => 1993, "C" => 1972}

    ↓ Main.rb:1: syntax error, unexpected =>, expecting '}' p {"Ruby" => 1993, "C" => 1972} ^~ ハッシュのつもりで {} を書いても、 ブロックの {} と勘違いされてエラーになります。 ※ p({~}) と引数を丸括弧で囲めば、ちゃんと動きます。 Rubyのエラーを整理(初級者向け) 2020/10/24 14
  10. 8進数に関するシンタックスエラー p 0o10 #=> 8 p 010 #=> 8 p

    080 # Invalid octal digit (訳: 有効でない8進数) Main.rb:3: Invalid octal digit p 080 ^~ Rubyでは、 0 始まりや 0o 始まりの数値は、8進数として解釈されます。 8進数は 0 〜 7 の数値で構成されるので、 8 や 9 があるとエラーになります。 ※多くの他⾔語でも、 0 か 0o のどちらかをサポートしています。 Rubyのエラーを整理(初級者向け) 2020/10/24 15
  11. Rubyの2進数のエラーが難しい 8進数に8や9が混ざると「 Invalid octal digit (訳: 有効でない8進数)」とわかりやすいエ ラー⽂がでるのに、2進数は異なっていて難しいです。 p 0b20101

    ↓ Main.rb:1: numeric literal without digits p 0b20101 ^~ Main.rb:1: syntax error, unexpected integer literal, expecting end-of-input p 0b20101 ^~~~~ エラー⽂を訳すと「 0b は数字のない数値リテラルだ」と「構⽂エラー︓予期せぬ数値 リテラル 20101 だ、ファイルの終わりを期待してたのに」となる。 Rubyのエラーを整理(初級者向け) 2020/10/24 16
  12. ArgumentError : i) 引数が異なるとき ⼤半のケースで、簡単に修正できるエラーです。 p 1024.to_s #=> "1024" p

    1024.to_s(2) #=> "10000000000" p 1024.to_s(:delimited, locale: :fr) #=> ArgumentError ※Railsでは to_s が拡張され桁区切りを指定できますが、素のRubyは出来ません。 ↓ Main.rb:3:in `to_s': wrong number of arguments (given 2, expected 0..1) (ArgumentError) from Main.rb:3:in `<main>' given 2, expected 0..1 は「2つの引数が与えられたけど、期待した個数は0~1個」の 意味ですね。 Rubyのエラーを整理(初級者向け) 2020/10/24 20
  13. 【余談】引数の数の違うときのエラー⽂の歴史 p 1024.to_s(:delimited, locale: :fr) #=> ArgumentError ↓ Before (Ruby

    〜2.2) wrong number of arguments (2 for 0..1) (ArgumentError) After (Ruby 2.3 〜 2.7) wrong number of arguments (given 2, expected 0..1) (ArgumentError) エラー⽂、だいぶわかりやすくなっている。 Rubyのエラーを整理(初級者向け) 2020/10/24 21
  14. ArgumentError : ii) 引数の数・クラス以外の性質に問題があるとき p [0] * 3 #=> [0,

    0, 0] p [0] * -1 # negative argument (ArgumentError) Main.rb:2:in `*': negative argument (ArgumentError) from Main.rb:2:in `<main>' プログラミング⾔語の中でも珍しい部類ですが、Rubyの演算⼦はメソッド。 Array (配列)のインスタンスメソッド * は引数に Integer を1つとって、 その引数の数だけ配列のサイズを倍にします。 Rubyのエラーを整理(初級者向け) 2020/10/24 22
  15. TypeError i) String#+ "1" + 1 ↓ Main.rb:1:in `+': no

    implicit conversion of Integer into String (TypeError) String クラスのメソッド + は、⽂字列どうしを結合する⽬的で、あるべき引数は String クラスです。しかし、実際の引数は Integer の 1 で、内部で Integer を String に変換できるか検証され「 Integer から String への暗黙の変換はできない」 と⾔われています。 【余談】このケースでは、引数側に⽂字列に変換する to_str が実装されていれば、内 部で暗黙の変換を⾏われます。 Rubyのエラーを整理(初級者向け) 2020/10/24 23
  16. TypeError ii) integer#+ 1 + "1" ↓ Main.rb:1:in `+': String

    can't be coerced into Integer (TypeError) from Main.rb:1:in `<main>' Integer と String を逆にすると、エラー⽂に聞き慣れない単語"coerced"がでてきま す。「 String は Integer に強制(的に変換)できなかった」みたいな意味。 マニアックな話ですが、数値クラスは異なるクラスの演算を⾏うときは、内部で coerce というメソッドを⽤いクラスを統⼀してから演算しています。 String クラス に coerce メソッドを実装してあげれば、エラーは解決します(!?) Rubyのエラーを整理(初級者向け) 2020/10/24 24
  17. その他の ArgumentError , TypeError (引数とってないもの) a = []; a <<

    a a.flatten #=> tried to flatten recursive array (ArgumentError) (1...5.0).max #=> cannot exclude non Integer end value (TypeError) Ruby の例外クラスは分類が粗すぎる or 細かすぎる - まめめも (2009/4/23) 実際、TypeError と ArgumentError の区別ってかなりいい加減に決められている ように感じます。 Rubyのエラーを整理(初級者向け) 2020/10/24 25
  18. NoMethodError a = [1, 2, 3] a.empty ↓ Main.rb:2:in `<main>':

    undefined method `empty' for [1, 2, 3]:Array (NoMethodError) Did you mean? empty? エラー⽂で、親切に教えてくれる。レシーバが Array クラスの [1, 2, 3] で、 empty というメソッドはないということを教えてくれている。さらに、 Did you mean? で 「もしかして、本当に書きたかったのは empty? だったのでは?」と教えてくれる。親 切。 Rubyのエラーを整理(初級者向け) 2020/10/24 28
  19. NoMethodError メソッドは、実は何らかのオブジェクトに紐付いています。 p self #=> main self.puts("Hello, world! from main")

    put Main.rb:3:in `<main>': undefined local variable or method `put' for main:Object (NameError) Did you mean? puts putc 普段よく使う puts は、 Kernel モジュールで定義され、その Kernel をインクルードし た Object クラスがあり、その Object か Object を継承しているクラスのインスタンス のprivateメソッド。 Rubyのエラーを整理(初級者向け) 2020/10/24 29
  20. NoMethodError の原因の例1 1. メソッド呼び出し時のメソッド名の誤り ※エラー⽂でスペルミス等の誤りを確認できる。 2. 想定外のクラスがレシーバに来てしまった レシーバ名の変数名の誤り 単数形・複数形を誤る( user

    と users ) スペルミスした未代⼊のインスタンス変数( nil を返す)など どこかでレシーバが nil になってしまった 代⼊されていないインスタンス変数など(スペルミス含む) Array / Hash の範囲外参照 nil を返すことがあるメソッド( uniq! など) Rubyのエラーを整理(初級者向け) 2020/10/24 31
  21. NoMethodError の原因の例2 3. オブジェクトにメソッドが正しく定義されていない。 メソッド定義でのスペルミスなど ※Did you mean?で、スペルミスのメソッド名がでてわかるかも。 必要なgemやライブラリがインストールされていない バージョンが違う

    バージョンが古くて新メソッドに対応できていない。 バージョンが新しく、廃⽌されたメソッドが使えなくなってる。 ※原因は、数え切れないほどあるはずで、⼀例です。 Rubyのエラーを整理(初級者向け) 2020/10/24 32
  22. NameError : 未定義のローカル変数やメソッド Rubyのメソッドは () を省略できる。 同名のローカル変数とメソッドを定義できる。 そのため、ローカル変数とメソッドは区別がつかないことがある。 hoge ↓

    Main.rb:1:in `<main>': undefined local variable or method `hoge' for main:Object (NameError) ローカル変数でも⾒つからず、メソッドを探索しても⾒つからなかった場合、このよ うなエラーになる。 Rubyのエラーを整理(初級者向け) 2020/10/24 34
  23. NameError : 初期化されていない定数のアクセス FOO ↓ Main.rb:1:in `<main>': uninitialized constant FOO

    (NameError) 訳: 初期化されていない定数 ⼤⽂字始まりは定数で、初期化されていない定数(クラスを含む)だと、このようなエラ ー⽂になる。 Rubyのエラーを整理(初級者向け) 2020/10/24 35
  24. 問題 何故エラーになるのでしょうか。 puts "Hello, world!" ↓ Main.rb:1:in `<main>': undefined method

    ` puts' for main:Object (NoMethodError) Did you mean? puts ヒント︓ エラー⽂に答えが書いてあります。 答え︓ 最初に全⾓スペースがあるので、 puts ではなく、 puts として1単語に認識 され、引数があるので、 puts メソッドを探してしまい未定義と怒られています。 Rubyのエラーを整理(初級者向け) 2020/10/24 37
  25. 答え puts "Hello, world!" ↓ Main.rb:1:in `<main>': undefined method `

    puts' for main:Object (NoMethodError) Did you mean? puts 答え︓ 最初に全⾓スペースがあるので、 puts ではなく、 puts として1単語に認識 され、(引数があるので変数ではなく) puts メソッドを探して⾒つからず「未定義 だ」と怒られています。 Rubyのエラーを整理(初級者向け) 2020/10/24 38
  26. いじわるな問題 何故エラーが起きているのでしょうか? def hello # 「ハロー」と表⽰する。 puts "ハロー" end hello

    Main.rb:5:in `<main>': undefined local variable or method `hello' for main:Object (NameError) Did you mean? hello ヒント1: hello は定義されていないと怒られています。 ヒント2: いじわるな問題です。 Rubyのエラーを整理(初級者向け) 2020/10/24 39
  27. いじわるな問題の答え def hello # 「ハロー」と表⽰する。 puts "ハロー" end hello Main.rb:5:in

    `<main>': undefined local variable or method `hello' for main:Object (NameError) Did you mean? hello hello の直後に全⾓スペースがあり、 hello ではなく、 hello というメソッド名で 定義されてしまっています。そのため、 hello で探しても、変数やメソッドが⾒つか らない状態です。 全⾓スペースが紛れたら、エラーがわかりにくくなる。 Rubyのエラーを整理(初級者向け) 2020/10/24 40
  28. 全⾓スペースの挙動・罠 Rubyに限らず、コードに全⾓スペースが紛れこむのは良くないです!! Ruby …… 「あいう」なども変数名に使えて、全⾓スペースもこれと同じ!! CSS …… Rubyと同じで、id名やclass名などに⽇本語・全⾓スペースが使えちゃう!! JavaScript ……

    全⾓スペースは、半⾓スペースと同じ扱い。紛れても⼤丈夫そう(?) CSSの場合、エラーにならずただ機能しなくなるだけで、かなり困りそう。 Rubyのエラーを整理(初級者向け) 2020/10/24 41
  29. 参考記事 - Rubyリファレンスマニュアル(通称るりま) Rubyリファレンスマニュアル ArgumentError (Ruby リファレンスマニュアル) TypeError (Ruby リファレンスマニュアル)

    NameError (Ruby リファレンスマニュアル) NoMethodError (Ruby リファレンスマニュアル) 変数と定数 (Ruby リファレンスマニュアル) Rubyのエラーを整理(初級者向け) 2020/10/24 44
  30. 参考記事・参考書籍 『Ruby 超⼊⾨』 2-7「エラーメッセージを読み解く」 『プロを⽬指すためのRuby⼊⾨』(通称: チェリー本 ) 第11章 Ruby の例外クラスは分類が粗すぎる

    or 細かすぎる - まめめも Ruby のエラーメッセージを読み解く(初⼼者向け)その 1 - Qiita Ruby のエラーメッセージを読み解く(初⼼者向け)その 2 - Qiita どの8進数リテラルが使えるかはプログラミング⾔語によって異なる - Qiita Rubyのエラーを整理(初級者向け) 2020/10/24 45
  31. File::Stat#modeの数値は8進数(lsコマンドの内部の数値) RubyのFile::Stat#modeで返ってくる数字について (by yupa-san) inode(7) - Linux manual page S_IFSOCK

    0140000 socket # s S_IFLNK 0120000 symbolic link # l S_IFREG 0100000 regular file # - S_IFBLK 0060000 block device # b S_IFDIR 0040000 directory # d S_IFCHR 0020000 character device # c S_IFIFO 0010000 FIFO # p 左から、1桁⽬が「8進数であることを⽰す0」で、2, 3桁⽬がファイルのタイプ、その 次が特別なパーミッションで、残り3桁が普通のパーミッション。 Rubyのエラーを整理(初級者向け) 2020/10/24 47
  32. Ruby以外の8進数リテラルの扱い C, Java, Python 2 …… 0 始まりだと、8進数。 Rust, Swift,

    Crystal, Python 3 …… 0o と書く必要あり。 Crystal, Python 3 …… 0 始まりだと、sytanx error。 Rust, Swift …… 0 始まりだと、10進数。 JavaScript …… 0o も 0 も書けるが、 0 始まりの処理が特殊。 8 か 9 が含まれていると10進数となり、 0 〜 7 のみだと8進数になる。 昔からあるメジャーな⾔語CやJavaでは 0 始まりは8進数でしたが、わかりにくいし古 い表記といえそうなので、 0o 始まりで書いた⽅が良さそうです。 Rubyのエラーを整理(初級者向け) 2020/10/24 48
  33. Pythonは、構⽂レベルで全⾓スペース禁⽌ Python 3.8.2 print("Hello, world!") print("全⾓スペースがあると実⾏されない") ↓ File "Main.py", line

    2 print("全⾓スペースがあると実⾏されない") ^ SyntaxError: invalid character in identifier Python …… 構⽂レベルで全⾓スペースを禁⽌。⽂字列やコメント等の以外のコード部 分に全⾓スペースがあると動かない。⽇本語は変数名に使えるみたい。 Rubyのエラーを整理(初級者向け) 2020/10/24 49
  34. C⾔語/C++での全⾓スペースの扱い C⾔語(C17 / clang version 10.0.0-4ubuntu1 3.0 512) C++(C17++ /

    clang version 10.0.0-4ubuntu1) ClangだとUnicodeの空⽩を許容しているそうで、実際に全⾓スペースが紛れていて も、「Unicode⽂字を空⽩⽂字として扱っているよ」と警告はでるが動作した。 Main.cpp:3:5: warning: treating Unicode character as whitespace [-Wunicode-whitespace] printf("Hello, world"); ^~ Rubyのエラーを整理(初級者向け) 2020/10/24 50
  35. JavaScriptの0始まりの8進数の不思議な挙動 JavaScript Node.js v12.18.3 console.log(080); // 80 console.log(010); // 8

    console.log(0o10); // 8 console.log(0O10); // 8 console.log(0o10); // SyntaxError: Invalid or unexpected token JavaScriptは 0 始まりの処理が特殊。 0 始まりでも 8 か 9 が含まれているときは10進数 0 始まりで 0 〜 7 のみで構成されるときは8進数。 0o 始まりだと、必ず8進数で、 8 などがあると構⽂エラー。 Rubyのエラーを整理(初級者向け) 2020/10/24 51
  36. Pythonの8進数の挙動 Python 3.8.2 print(0o10) # 8 print(0O10) # 8 print(010)

    # SyntaxError: leading zeros in decimal integer literals # are not permitted; use an 0o prefix for octal integers Pythonは、2 -> 3にかけて 010 のような 0 始まりの8進数を構⽂レベルで禁⽌した。 Rubyのエラーを整理(初級者向け) 2020/10/24 52
  37. RustやSwiftでは、 0 始まりは10進数の扱い Rust fn main(){ let n = 010;

    println!("{}", n); } print(010) // 010 Rubyのエラーを整理(初級者向け) 2020/10/24 53
  38. 0始まりで書きたくなる状況は、⽂字列の⽅が良さそう birthday = 0107 breakfast = 0730 tel = 0120_0000_0000

    post_code = 010_0000 こういう電話番号や郵便番号は、0始まりがあったり、計算するものでもなかったり、 様々な理由により、⽂字列で管理した⽅が良いと思われる。 【参考】invalid octal digit エラーが起きたとき - Qiita datetime: DateTime.new(2015,10,08,00,00,00) Rubyのエラーを整理(初級者向け) 2020/10/24 54
  39. numeric literal without digits Bug #2407: numeric literal without digits

    - Ruby master - Ruby Issue Tracking System 0o_, 0x_ などprefixの直後に _ が来た場合には、このエラーを意 図的に出しています。_というのは数値表現の数字と数字の間に⼊る もので、prefixと数字の間に⼊るものではない、と考えたからです。 「エラーメッセージがわかりにくい」とか「prefixと数値の間に _ が⼊って何が悪い」などのいう指摘は、今後考慮しないとは⾔いま せんが、少なくとも現時点ではこれは仕様です。 10年以上前の2009年のMatz⽒の発⾔。 0o_101 の⽅がわかりやすいような? ⾃分は書く機会がないけど、機会があったらハマる気がする。 Rubyのエラーを整理(初級者向け) 2020/10/24 55
  40. NoMethodError 2.prime? ↓ Main.rb:1:in `<main>': undefined method `prime?' for 2:Integer

    (NoMethodError) 訳︓ Integer クラスの 2 に prime? メソッドは定義されていません。 Integer#prime? を使うには、 require 'prime' でライブラリをロードして Integer ク ラスを拡張する必要がある。 Rubyのエラーを整理(初級者向け) 2020/10/24 57