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. 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 16
  12. 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 17
  13. 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 18
  14. 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 22
  15. 【余談】引数の数の違うときのエラー⽂の歴史 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 23
  16. 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 24
  17. 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 26
  18. 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 27
  19. ArgumentError , TypeError は、いい加減? Ruby の例外クラスは分類が粗すぎる or 細かすぎる - まめめも

    (2009/4/23) 実際、TypeError と ArgumentError の区別ってかなりいい加減に決められている ように感じます。 a = []; a << a a.flatten #=> tried to flatten recursive array (ArgumentError) (1...5.0).max #=> cannot exclude non Integer end value (TypeError) このように ArgumentError , TypeError は、引数がなくても⽣じます。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 28
  20. 参考記事・参考書籍 『Ruby 超⼊⾨』 2-7「エラーメッセージを読み解く」 『プロを⽬指すためのRuby⼊⾨』(通称: チェリー本 ) 第11章 Ruby の例外クラスは分類が粗すぎる

    or 細かすぎる - まめめも Ruby のエラーメッセージを読み解く(初⼼者向け)その 1 - Qiita Ruby のエラーメッセージを読み解く(初⼼者向け)その 2 - Qiita どの8進数リテラルが使えるかはプログラミング⾔語によって異なる - Qiita Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 31
  21. 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 33
  22. 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 34
  23. RustやSwiftでは、 0 始まりは10進数の扱い Rust fn main(){ let n = 010;

    println!("{}", n); } print(010) // 010 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 35
  24. 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 36
  25. 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 37