こんにちは、hachi8833です。「Rubyスタイルガイドを読む」、今回は文法編です。
文法の項は70ほどもあるので、何回かに分割しようと思います。
- Rubyスタイルガイドを読む: 総もくじ
- 前回: Rubyスタイルガイドを読む: ソースコードレイアウト(2)インデント、記号
- 次回: Rubyスタイルガイドを読む: 文法(2)アンダースコア、多重代入、三項演算子、if/unless
文法(1) メソッド定義、引数、多重代入など
2-01【統一】::
は、定数(クラスやモジュールも含む)、コンストラクタ(Array()
やNokogiri::HTML()
など)の参照にのみ使う
Use
::
only to reference constants(this includes classes and modules) and constructors (likeArray()
orNokogiri::HTML()
).
Do not use::
for regular method invocation.
通常のメソッド呼び出しでも::
を使うと読みづらくなるのでスタイルを設定していると考えられます。
# 不可
SomeClass::some_method
some_object::some_method
# 良好
SomeClass.some_method
some_object.some_method
SomeModule::SomeClass::SOME_CONST
SomeModule::SomeClass()
2-02【統一】メソッド定義にパラメータがない場合は、def
行のメソッドにかっこ()
をつけない
Use
def
with parentheses when there are parameters. Omit the parentheses when the method doesn't accept any parameters.
逆にメソッド定義にパラメータがある場合は明示的に()
をつけます。
# 不可
def some_method()
# body omitted
end
# 良好
def some_method
# body omitted
end
# 不可
def some_method_with_parameters param1, param2
# body omitted
end
# 良好
def some_method_with_parameters(param1, param2)
# body omitted
end
Rubyではメソッド呼び出しなどでパラメータのかっこを省略できますし、呼び出しではかっこを省略するスタイルが広く使われていますが、本スタイルではメソッドの定義・呼び出しともにかっこを省略しないスタイルになっています(メソッド呼び出しについては後述)。
2-03【統一】メソッド呼び出しのかっこ()
は省略しない(特に第1引数がかっこ()
を使った式の場合)
Use parentheses around the arguments of method invocations, especially if the first argument begins with an open parenthesis
(
, as inf((3 + 2) + 1)
.
# 不可
x = Math.sin y
# 良好
x = Math.sin(y)
# 不可
array.delete e
# 良好
array.delete(e)
# 不可
temperance = Person.new 'Temperance', 30
# 良好
temperance = Person.new('Temperance', 30)
「特に第1引数が...」のサンプルを以下に取り出してみました。
f (3 + 2) + 1 # 読みにくい
f((3 + 2) + 1) # 読みやすい
2-03a 【例外】次の場合にのみかっこ()
を省略する
- 引数のないメソッド呼び出し
# 不可
Kernel.exit!()
2.even?()
fork()
'test'.upcase()
# 良好
Kernel.exit!
2.even?
fork
'test'.upcase
- 内部DSLの一部となっているメソッド呼び出し
DSL(ドメイン固有言語: Domain Specific Language)にはRake、Rails、RSpecなどが該当します。RubyではこうしたDSL処理系を比較的簡単に作ることができます。
# 不可
expect(bowling.score).to eq 0
# 良好
expect(bowling.score).to eq(0)
- ステータスをキーワードで指定するメソッド
確かに、:name
などのシンボルならかっこがなくても十分視認性が確保されそうですし、むしろかっこがあるとうるさく見えそうです。
class Person
# 不可
attr_reader(:name, :age)
# 良好
attr_reader :name, :age
# 本文は省略
end
# 不可
puts(temperance.age)
# 良好
puts temperance.age
他の言語の経験者は最初なかなか慣れないかもしれませんが、慣れるとRubyのメソッド呼び出しにおけるかっこ省略スタイルはむしろ読みやすく感じられます(注: 個人の感想です)。
とはいうものの、込み入ったメソッド呼び出しでかっこをつけないと読みづらくなったり誤動作したりすることがあるので、そういった場合にはかっこをつけることになります。
このあたりは既存のコードや会社のコーディングスタイルに合わせて変えることもあるかと思います。
2-04【統一】オプション引数は引数リストの末尾に置く
Define optional arguments at the end of the list of arguments. Ruby has some unexpected results when calling methods that have optional arguments at the front of the list.
オプション引数(optional arguments)とは、a = 1
のようにデフォルト値を指定した引数のことであり、その引数の値を省略したときにデフォルト値が使われます。
# 不可
def some_method(a = 1, b = 2, c, d)
puts "#{a}, #{b}, #{c}, #{d}"
end
some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'w, 2, x, y' #これがマズい結果になりやすい
some_method('w', 'x', 'y', 'z') # => 'w, x, y, z'
# 良好
def some_method(c, d, a = 1, b = 2)
puts "#{a}, #{b}, #{c}, #{d}"
end
some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'y, 2, w, x'
some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
なかなかわかりにくいサンプルですが、「良好」の方ではa = 1
とb = 2
というオプション引数を引数リストの末尾においていますので、引数リストは「c, d, a, b」といささか異様な順序になっています。
逆に「不可」の方ではsome_method('w', 'x') # => '1, 2, w, x'
のように、引数a
に"w"を渡したつもりがデフォルト値の1
が使われ、渡した"w"がc
に渡されるという挙動になっています。これは場合によっては望ましくない動作になると思います。
どちらもメソッド呼び出しで引数をすべて渡すと正常に動作するので、引数を減らしてみて初めて気づくということがありそうです。
この動作はRubyの仕様によるものであり、不具合を避けるためにこのスタイルを決めたと理解しました。
参考
以下は主にRubyのキーワード引数についての記事ですが、近年のRubyでよく変更されている機能なので、オプション引数について理解するときに合わせて読むとよいと思います。
2-05【統一】メソッドに論理値を引数として渡す場合はキーワード引数を使う
2018/09/16に上のコミットが追加されたので反映しました。
# 不可
def some_method(bar = false)
puts bar
end
# 不可: キーワード引数が導入される前に多用されていたハック
def some_method(options = {})
bar = options.fetch(:bar, false)
puts bar
end
# 良好
def some_method(bar: false)
puts bar
end
some_method # => false
some_method(bar: true) # => true
2-06【統一】キーワード引数の方がオプション引数よりも望ましい
commit: Prefer keyword arguments over optional arguments · rubocop-hq/ruby-style-guide@508e506
2018/09/16に上のコミットが追加されたので反映しました。
上の「2-04【統一】オプション引数は引数リストの末尾に置く」が残されていることから、オプション引数の利用は一応認められてはいるものの、キーワード引数の利用が推奨されています。
# 不可
def some_method(a, b = 5, c = 1)
# (略)
end
# 良好
def some_method(a, b: 5, c: 1)
# (略)
end
2-07【統一】変数定義での多重代入は避ける
Avoid the use of parallel assignment for defining variables. Parallel
assignment is allowed when it is the return of a method call, used with
the splat operator, or when used to swap variable assignment. Parallel
assignment is less readable than separate assignment.
多重代入自体はコードを簡潔に書くのに有用ですが、変数定義では避けるようにとの指示です。
たぶん多重代入だと変数定義らしく見えないからではないかと推測しています。変数が多くなったときにもわかりにくくなりそうですね。
多重代入は以下の場合に認められます。
- メソッドの返り値
- splat演算子としての
*
- 変数の値の入れ替え
# 不可
a, b, c, d = 'foo', 'bar', 'baz', 'foobar'
# 良好
a = 'foo'
b = 'bar'
c = 'baz'
d = 'foobar'
# 良好 - 変数の値入れ替えに使う場合
# Swapping variable assignment is a special case because it will allow you to
# swap the values that are assigned to each variable.
a = 'foo'
b = 'bar'
a, b = b, a
puts a # => 'bar'
puts b # => 'foo'
# 良好 - メソッドの返り値
def multi_return
[1, 2]
end
first, second = multi_return
# 良好 - splat演算子で使う場合
first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4]
hello_array = *'Hello' # => ["Hello"]
a = *(1..3) # => [1, 2, 3]
関連記事
- Rubyスタイルガイドを読む: ソースコードレイアウト(1)エンコード、クラス定義、スペース
- Rubyスタイルガイドを読む: ソースコードレイアウト(2)インデント、記号
- Rubyスタイルガイドを読む: 文法(1)メソッド定義、引数、多重代入
- Rubyスタイルガイドを読む: 文法(2)アンダースコア、多重代入、三項演算子、if/unless
- Rubyスタイルガイドを読む: 文法(3)演算子とif/unless
- Rubyスタイルガイドを読む: 文法(4)ループ
- Rubyスタイルガイドを読む: 文法(5)ブロック、proc
- Rubyスタイルガイドを読む: 文法(6)演算子など
- Rubyスタイルガイドを読む: 文法(7)lambda、標準入出力など
- Rubyスタイルガイドを読む: 文法(8)配列や論理値など
- Rubyスタイルガイドを読む: 命名
- Rubyスタイルガイドを読む: コメント、アノテーション、マジックコメント
- Rubyスタイルガイドを読む: クラスとモジュール(1)構造
- Rubyスタイルガイドを読む: クラスとモジュール(2)クラス設計・アクセサ・ダックタイピングなど
- Rubyスタイルガイドを読む: クラスとモジュール(3)クラスメソッド、スコープ、エイリアスなど
- Rubyスタイルガイドを読む: 例外処理
- Rubyスタイルガイドを読む: コレクション(Array、Hash、Setなど)
- Rubyスタイルガイドを読む: 数値、文字列、日時(日付・時刻・時間)
- Rubyスタイルガイドを読む: 正規表現、%リテラル、メタプログラミング(最終回)