🤔

Java系エンジニアがRubyでつまづいたこと

2023/08/15に公開

&.と書くoptional chaining的なものの挙動が変

後続処理の違い

TypeScriptやKotlinでは、?.ですが、それと同じと思っていたら、違いました。

let a: { s: string } | null = {s: "hello world"}
a?.s.length // 11
a = null
a?.s.length // undefined

TypeScriptではこうですが、これと同じノリでRubyで書いたら…

require 'ostruct'
a = OpenStruct.new(s: "hello world")
a&.s.length # 11
a = nil
a&.s.length # undefined method `length' for nil:NilClass (NoMethodError)

エラーになりました…。

a&.s&.length # 11

Rubyではこう書かないといけないようです。

case class A(s: String)
var a: Option[A] = Some(A("hello world"))
// a.map(x => x.s.length)のシンタックスシュガーです
a.map(_.s.length) // Option[Int] = Some(11)
a = None
a.map(_.s.length) // Option[Int] = None

Scalaで同様のコードを書くとこうです。値があった場合のみ処理されるので、そのあたりがTypeScriptと使う脳が一緒ですね。

配列へのアクセスが変

arr = [1,2,3]
arr&.[2] # syntax error, unexpected '[' (SyntaxError)       
arr&.[](2) # 3

RubyでOptional Chainingして、添字でアクセスする、となるとなった途端に書き方が変わる…。

const arr = [1,2,3]
arr?.[2]

TypeScriptでは、添字でアクセスする場合でも変わらない。

:コロンが前なのか後ろなのかルールが覚えられない

irb(main):001:0> {"key": 1}
=> {:key=>1}
irb(main):002:0> {key: 1}
=> {:key=>1}
irb(main):003:0> {:key => 1}
=> {:key=>1}
irb(main):004:0> {"key" => 1}
=> {"key"=>1}
irb(main):005:0> {:"key" => 1}
=> {:key=>1}

ハッシュはこうだし

メソッドの引数も

def method1(aaa:)

となるし…

これはなかなか覚えられませんでした

injectとも書けて、reduceともかける

脳に定着せず、学習コストが上がるような…

raise~rescue,とthrow~catchの違い

raise~rescue,とthrow~catchの違い、本当にわかってる?まとめた - Qiita
Javaの文脈でthrowと言ったら、誤解されましたので、Rubyでの開発をする場合は、throwとraiseの違いを意識しましょう。

aliasは、alias_methodの違いなど

  # カンマが要らない。カンマつけるとエラーになる。メソッドではなく、キーワードらしい。
  alias rename_method1 method1
  # シンボルにしてもいいみたい
  alias :rename_method1 :method1
  # alias :rename_method1, :method1 # error

  # alias_methodというメソッドのためカンマをつける必要がある。メソッドへの引数なので、:をつけてシンボルにする必要がある。
  # IntelliJは、メソッドの場合は、明るいほうのオレンジにしてくれるみたい。
  alias_method :rename_method2, :method2
  # alias_method rename_method2, method2 # error
  # alias_method :rename_method2 :method2 # error

  # Ruby標準のグローバルなメソッドじゃないものはオレンジ色にならないっぽい?
  attr_reader2 :variable1, :variable2, :variable3

Screenshot from 2023-08-15 17-06-55.png
IntelliJでの見え方

lambdaとprocとblockの違いが難しい

Rubyの ブロック、Proc.new、lambdaの違い - Qiita

static privateメソッドを書くのにクセがある

Rubyでprivateなクラスメソッドを定義するには - ESM アジャイル事業部 開発者ブログ

正しくrequireできているかを知る術が無い

IDEでオートインポートされない(される設定があるなら教えてほしい)ので、自分で書く必要がある。
Zeitwerkなどを使っていて、requireを書く必要を無くす、というのが解決策なんでしょうね…きっと。

ラムダの記法が独特

->(e) { }

ラムダの->なぜそこにつけた。

===という記号は左右対称なのに、実行結果が「左右対称」じゃない

Rubyの===演算子についてまとめてみた|TechRacho by BPS株式会社

mapのなかでreturnを書いてしまうと意図しない挙動になる

このあたりがメソッドとブロックの違いのようです。
解決法は、以下に記載がある
リファクタリングと妥協を繰り返して君だけの.rubocop.ymlを作ろう - ARMERIA

ブロック内のnextは、次の処理へ進むという意味と思いきや、そうでもないときがある

def block_example
  yield
  puts "after block"
end

block_example do
  puts "hello world"
  next
  puts "not executed"
end

例として、こういったものです。
実行結果は、
hello world
after block
となります。
nextとはいったい…。

unless使いこなせない

if !(hogehoge)じゃダメなのかな…。いつも見慣れたコードと違うコードになってしまい、読むのに時間がかかってしまいます。

インデント

privateがインデントの階層が同じレベルにあるので分かりづらい。
moduleで階層が深くなってしまう。

numerable#each_with_objectとEnumerable#reduceの引数の位置が違う

https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/reduce.html
https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/each_with_object.html

ブロックをオブジェクトとして、渡したり取得する場合、&で渡す

def call_block(&block)
  block.call
end

call_block { puts "Hello, block!" }

C++の参照渡しのように見えるが、そうではない。

@1個でも使い方次第で、クラスインスタンス変数とインスタンス変数が変わる

class A
  @value = 1
  def self.static_value
    @value
  end
  def self.static_set_value(v)
    @value = v
  end
  def value
    @value
  end
  def set_value(v)
    @value = v
  end
end

a0 = A.new

A.static_set_value(5)
a0.set_value(6)

puts "A.static_value"
puts A.static_value # 5

puts "a0.value"
puts a0.value # 6

staticメソッドかそうじゃないかでインスタンス変数かクラスインスタンス変数かが変わる…

引数に&blockと書いていない場合でも、ブロックを引数に取れる

def my_method
  yield("hello", 1)
  yield("world", 2)
end

my_method do |message, number|
  puts "#{message}, #{number}"
end

引数に&blockと書いていないから、メソッドシグネチャが分かりづらい。
blockを受け取って処理できるということが実装を見ないと分からない。

Rubyのprivateメソッド、protectedメソッドの挙動がJavaとは違う

JavaやC#の常識が通用しないRubyのprivateメソッド - give IT a try

doをつけるのかつけないのかよくわからない

class A do
  def method1 do
    puts("hello world")
  end
end

と書いてしまう…

ヒアドキュメントの記号が3つもある

<<
<<-
<<~

の3種類もある…

if式があるのに、3項演算子もある

KotlinやScalaには、3項演算子が無い代わりに、ifが式となっています。if式でいいんじゃ…という気持ちになります。

includeとextendは使い分ける

【Ruby】includeとextendの違い。クラスメソッドとインスタンスメソッドの違いと実例。 - Qiita
Scalaのtraitを使うときみたいに、全部extendしてました…。使い分けます。

末尾にnilを置いておくと、Style/GuardClauseで怒られるのを回避できる

def method1
  if @variable1.nil?
    raise "error1"
  end

  if @variable2.nil?
    raise "error2"
  end

  if @variable3.nil?
    raise "error3"
  end

  # 以下コードに変更されるのを防げる
  # unless @variable3.nil?
  #   return
  # end
  # 
  # "error3"
  nil
end

Array#concatは破壊的に連結

https://docs.ruby-lang.org/ja/latest/method/Array/i/concat.html
配列 other を自身の末尾に 破壊的 に連結します。
なんだってー?!(JavaのString脳、JavaScriptのArray/String脳)

GUIデバッグが標準ではできない

debaseなどのGemを追加で入れないといけなく、GUIデバッグ派の人は、デバッグのための環境構築から入らないといけない。
IntellJでは、debase、VSCodeでは、vscode-rdbgを使う必要がある。

GitHubで編集を提案

Discussion