【Ruby】「オブジェクトへの参照」と「変数の中身」


はじめに

Rubyを使用していると、「オブジェクトへの参照」という概念が出てくると思います。

例えば、以下のようなコードがあったとします。

sample.rb
# 渡された文字列を破壊的に大文字に変換するメソッドを定義する
def replace!(str)
  str.upcase!
end

str = 'sample'
# 変数strにreplaceメソッドを適用する
replace!(str) #=> "SAMPLE"

この時、replaceメソッドの引数であるstrに渡されたのは、「変数strに格納されているStringオブジェクト'sample'への参照である」いうような説明がよくされると思います。

しかし、この説明を聞いても

  • 「オブジェクトへの参照とはそもそも一体どういうことなのか?」
  • 「変数に格納されているオブジェクトへの参照とはどういうこと?」

と思っている人は多いのではないでしょうか?

今回はそのような方へ向けて、

  • オブジェクトへの参照
  • 変数の正体
  • 参照の値渡し

といったことをわかりやすく説明していきます。

ぜひ最後までご覧になってください。

対象読者

  • Rubyを学び始めた人
  • プログラミング始めたての人
  • 「オブジェクトへの参照」がよくわからない人

本記事の内容

1. オブジェクトへの参照とは?
2. 変数の正体
3. メソッドの引数へ渡されるもの
4. まとめ
5. 最後に
6. 参照サイト

1. オブジェクトへの参照とは?

「オブジェクトへの参照」とは簡単にいうと「オブジェクトの居場所にアクセスする」ことを指しています。

まず、Rubyはオブジェクト指向言語であるため、配列や文字列はもちろん、数値であったり、nilも含め、全ての値がオブジェクトとして扱われます。

そしてこのようなオブジェクト指向言語では、あるオブジェクトを定義すると、それと同時にオブジェクトを記憶するためのメモリ領域というものが定められます。

簡単に言うと、各オブジェクトに住所が定められるというようなイメージです。

# String(文字列)オブジェクトの'Hello'を生成
'Hello'

そして、こうして定められたオブジェクトの住所にアクセスすることを「オブジェクトへの参照」と呼ぶのです。

ざっくり言ってしまうと、「オブジェクトへの参照=オブジェクトの居場所」のことです

2. 変数の正体

次に、先ほど出てきたメモリ領域と変数との関係性について考えていきます。

例えば、以下のような状況を考えてみます。

# 変数aに'test'を代入する
a = 'test'

この時、裏側では次のようなことが起こっています。

① String(文字列)オブジェクトが作成される(中身は'test')
② その住所(メモリ領域)が変数aに格納される

つまり、変数の実体とは「メモリ領域」であり、さらにその変数に入っている値は実は「変数に格納されているオブジェクト(ここでは'test')の住所」であるということが言えます。

変数aの中には、文字列オブジェクトそのものが入っていないということをちゃんと押さえておきましょう。

3. メソッドの引数へ渡されるもの

ではここから、この変数をメソッドの引数に渡す場面を考えてみましょう。

一番最初に挙げた例を元に考えてみましょう。

sample.rb
# 渡された文字列を破壊的に大文字に変換するメソッドを定義する
def replace!(str)
  str.upcase!
end

# 変数strに文字列'sample'を代入する
str = 'sample'

# 変数strにreplaceメソッドを適用する
replace!(str) #=> "SAMPLE"

この時、何が起こっているのでしょうか?

まず、最初に「replace」メソッドが定義されていますが、このメソッド内に含まれる引数「str」も当然変数なので、先ほど述べたように当然メモリ上に領域が確保されます。
(この領域は固定された場所ではなく、メソッドが呼ばれると確保され、メソッドの実行が終わると解放されます。

そして、メソッドが定義された時点ではこの引数は仮のものなので(仮引数)、その中にはまだ何も入っていません。
ここではあくまで「メモリ上に領域を確保しただけ」という状態です

そして次に、先ほどと同様に文字列「sample」と変数strがセットされます。

ここまでをまとめると以下のようになります。

次に、「replace!メソッド」を呼び出すと、その直後の状態は図で表すと、以下のようになります。

ここでのポイントは

  • 変数(実引数)strの値は、文字列オブジェクトへの参照であり、それがそのまま引数(仮引数)strにコピーされる

ということです。

そしてこのように、「変数に格納されているオブジェクトへの参照」が引数として渡されることを「参照の値渡し」と呼びます。

この参照の値渡しが行われると、当然ですが変数strと引数strは同じオブジェクトを共有していることになります。

そのため、引数strの値が変更されると、連動してその引数の値の住所に住んでいるオブジェクトの中身も変更されることになります。
(専門的な言い方をすれば、引数strの参照先であるオブジェクトが変更される

今回の場合だと、「replace!メソッド」の中身である「str.upcase!」が実行されると以下のようになります。

これを見ると、引数strの値は変わっていませんが、その引数が指し示すオブジェクトの中身がしっかりと変更されていることがわかると思います。

4. まとめ

  • オブジェクトへの参照とは「オブジェクトの居場所にアクセスする」こと
  • 変数の実体は「その変数に格納されているオブジェクトへの参照」
  • 参照の値渡しとは「変数に格納されているオブジェクトへの参照がそのまま引数に渡される」こと

5. 最後に

本記事の内容がみなさんの参考になれば嬉しいです。

最後までご覧いただきありがとうございました。

6. 参考文献

Qiita記事: 【Ruby】メソッドの引数は値渡し?参照渡し?
Qiita記事: Rubyの破壊的メソッドと参照の値渡し
Rubyist Magazine: 値渡しと参照渡しの違いを理解する