In Ruby, why does inspect() print out some kind of object id which is different from what object_id() gives?
Asked Answered
H

3

32

When the p function is used to print out an object, it may give an ID, and it is different from what object_id() gives. What is the reason for the different numbers?

Update: 0x4684abc is different from 36971870, which is 0x234255E

>> a = Point.new
=> #<Point:0x4684abc>

>> a.object_id
=> 36971870

>> a.__id__
=> 36971870

>> "%X" % a.object_id
=> "234255E"
Harleigh answered 12/5, 2010 at 12:10 Comment(0)
B
53

The default implementation of inspect calls the default implementation of to_s, which just shows the hexadecimal value of the object directly, as seen in the Object#to_s docs (click on the method description to reveal the source).

Meanwhile the comments in the C source underlying the implementation of object_id shows that there are different “namespaces” for Ruby values and object ids, depending on the type of the object (e.g. the lowest bit seems to be zero for all but Fixnums). You can see that in Object#object_id docs (click to reveal the source).

From there we can see that in the “object id space” (returned by object_id) the ids of objects start from the second bit on the right (with the first bit being zero), but in “value space” (used by inspect) they start from the third bit on the right (with the first two bits zero). So, to convert the values from the “object id space” to the “value space”, we can shift the object_id to the left by one bit and get the same result that is shown by inspect:

> '%x' % (36971870 << 1)
=> "4684abc"

> a = Foo.new
=> #<Foo:0x5cfe4>
> '%x' % (a.object_id << 1)
=> "5cfe4"

Note: The details about object_id were correct at the time (2010), but aren't anymore after newer Ruby versions have decoupled object_id from memory addresses. Now the answer is more along the lines "because object_id is generated on demand". See comments.

Bitt answered 12/5, 2010 at 12:53 Comment(4)
those ruby-doc.org links are dead now :-(Barriebarrientos
@Barriebarrientos I couldn't find directs links to method sources anymore, edited the post with indirect links.Bitt
This answer is no longer correct for Ruby versions 2.7 and greater. This blog post gives a great explanation. Recent Ruby versions have decoupled object_id from the object's actual memory address.Solipsism
@Solipsism Thanks, you are correct. I have made a note of this in the answer (but left it mostly as it was for historical reference).Bitt
B
0
0x234255E

=>36971870

It's not different, it's the hexadecimal representation of the memory address:-)

Backscratcher answered 12/5, 2010 at 12:13 Comment(2)
0x4684abc = 2 * 0x234255E, but why? ))Marcellusmarcelo
@aaz: Because object_id shifts the object's address to the left by one bit (which is the same as multiplying by two). See my answer for link to source.Bitt
S
0

Ruby version 2.7 decoupled the object's object_id from the memory address itself, so you can't transform an object_id into a memory address or vice versa. In fact, the object's actual address can change while its object_id remains the same.

That said, depending on your Ruby version, the ObjectSpace module might be able to return an object's memory address. Note: this library changes frequently, so most likely you should only use it in the console.

# Using Ruby version 2.7.8 -- this code may not work in later versions!

require "objspace"
require "json"

object = Object.new
#=> #<Object:0x00007ff174981a08>

object_internals_json = ObjectSpace.dump(object)
#=> "{\"address\":\"0x7ff174981a08\", \"type\":\"OBJECT\", \"class\":\"0x7ff18d991250\", \"ivars\":0, \"memsize\":40, \"flags\":{\"wb_protected\":true}}\n"

JSON.parse(object_internals_json)["address"]
#=> "0x7ff174981a08"
Solipsism answered 10/8, 2023 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.