How can I trace string creation in Ruby? - ruby

In this answer to a separate question, I said that if you do
name = "Rohit " "Sharma"
you don't create two String objects with the contents "Rohit " and "Sharma" that combine to create a new String object with the contents "Rohit Sharma", but that you only create a single String object to start off with.
But it's only a book telling me that, rather than manually verifying it.
How would you be able to log the creation of a String?
I tried using
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
string = "Insert content here"
But only got
c-return -:3 set_trace_func Kernel
line -:5
And "Programming Ruby 1.9" (the Pickaxe) says that modifying Class#new wouldn't work for strings, as they're constructed without using new.

You can't log this, since this concatenation doesn't happen at runtime. It happens at parse time.
In other words: regardless of whatever code you write to log this, the concatenation will have happened long before your code ever gets run.
In yet other words: you trust that there's only one string object created the same way that you trust that 42 only creates one Fixnum object with the value 42, not 42 Fixnum objects with the value 1: the ISO Ruby Language Specification says so, and the source code of every single existing Ruby implementation says so, and every Ruby book ever written says so.

This doesn't trace string creation as such, but the following suggests that the concatenation occurs before any string objects are created:
The following (I read about this in the Pickaxe, in the section "Behind the Curtain: The Ruby VM") works in YARV - I don't think it'd work in any other implementations of ruby:
irb(main):003:0> uncompiled_code = 'name = "Rohit " "Sharma"'
=> "name = \"Rohit \" \"Sharma\""
irb(main):004:0> code = RubyVM::InstructionSequence.compile(uncompiled_code)
=> <RubyVM::InstructionSequence:<compiled>#<compiled>>
irb(main):005:0> puts code.disassemble
== disasm: <RubyVM::InstructionSequence:<compiled>#<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] name
0000 trace 1 ( 1)
0002 putstring "Rohit Sharma"
0004 dup
0005 setlocal name
0007 leave
=> nil

Related

+= operator appears to modify frozen string

I am using ruby freeze method. As far as the definition of freeze is considered, it freezes the value of the object on which it is called. We can not modify the value of that object after it. I have to achieve same task, I have a an object and I am executing following code
a = "Test"
a.freeze
a += "this string"
puts a
This gives outputs as follows:
Test this string
[Finished in 0.0s]
Why it is modifying my frozen string?
Nothing is modifying your frozen String
You are re-assigning a to a new String with
a += "this string"
which is internally the same in Ruby as
a = a + "this string"
When you add two String objects in Ruby, it will create a new String containing the result (this is normal behaviour for + operator on most objects that support it). That leaves the original "Test" and "this string" values unchanged. The original, frozen String (containing "Test") will remain in memory until it is garbage collected. It can be collected because you have lost all references to it.
If you attempted to modify the object in place like this:
a << "this string"
then you should see an error message RuntimeError: can't modify frozen String
Basically, you have confused a, the local variable, with the String object to which it is pointing. Local variables can be re-assigned at any time, independently of the objects stored by Ruby. You can verify this is what has happened in your case by inspecting a.object_id before and after your a +=... line.
The freeze method prevents you from changing an object, it turns an object into a constant.
s1 = "its testing"
s1.freeze
puts "Object ID ===", s1.obejct_id
So, after freezing an object, an attempt to modify it results in TypeError.
s1 << "testing again"
it will give, RuntimeError: can't modify frozen String
BUT,
freeze operates on an object reference, not on a variable
s1 += "New Testing"
puts "Object ID ===", s1.obejct_id
will point to evaluated to a new object and also check its object ID.
For detailed information refer this site,
http://rubylearning.com/satishtalim/mutable_and_immutable_objects.html

Are Hashes in Ruby passed by reference? [duplicate]

#user.update_languages(params[:language][:language1],
params[:language][:language2],
params[:language][:language3])
lang_errors = #user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------"
+ lang_errors.full_messages.inspect
if params[:user]
#user.state = params[:user][:state]
success = success & #user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------"
+ lang_errors.full_messages.inspect
if lang_errors.full_messages.empty?
#user object adds errors to the lang_errors variable in the update_lanugages method.
when I perform a save on the #user object I lose the errors that were initially stored in the lang_errors variable.
Though what I am attempting to do would be more of a hack (which does not seem to be working). I would like to understand why the variable values are washed out. I understand pass by reference so I would like to know how the value can be held in that variable without being washed out.
The other answerers are all correct, but a friend asked me to explain this to him and what it really boils down to is how Ruby handles variables, so I thought I would share some simple pictures / explanations I wrote for him (apologies for the length and probably some oversimplification):
Q1: What happens when you assign a new variable str to a value of 'foo'?
str = 'foo'
str.object_id # => 2000
A: A label called str is created that points at the object 'foo', which for the state of this Ruby interpreter happens to be at memory location 2000.
Q2: What happens when you assign the existing variable str to a new object using =?
str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002
A: The label str now points to a different object.
Q3: What happens when you assign a new variable = to str?
str2 = str
str2.object_id # => 2002
A: A new label called str2 is created that points at the same object as str.
Q4: What happens if the object referenced by str and str2 gets changed?
str2.replace 'baz'
str2 # => 'baz'
str # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002
A: Both labels still point at the same object, but that object itself has mutated (its contents have changed to be something else).
How does this relate to the original question?
It's basically the same as what happens in Q3/Q4; the method gets its own private copy of the variable / label (str2) that gets passed in to it (str). It can't change which object the label str points to, but it can change the contents of the object that they both reference to contain else:
str = 'foo'
def mutate(str2)
puts "str2: #{str2.object_id}"
str2.replace 'bar'
str2 = 'baz'
puts "str2: #{str2.object_id}"
end
str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004
In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.
Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)
Ruby uses "pass by object reference"
(Using Python's terminology.)
To say Ruby uses "pass by value" or "pass by reference" isn't really descriptive enough to be helpful. I think as most people know it these days, that terminology ("value" vs "reference") comes from C++.
In C++, "pass by value" means the function gets a copy of the variable and any changes to the copy don't change the original. That's true for objects too. If you pass an object variable by value then the whole object (including all of its members) get copied and any changes to the members don't change those members on the original object. (It's different if you pass a pointer by value but Ruby doesn't have pointers anyway, AFAIK.)
class A {
public:
int x;
};
void inc(A arg) {
arg.x++;
printf("in inc: %d\n", arg.x); // => 6
}
void inc(A* arg) {
arg->x++;
printf("in inc: %d\n", arg->x); // => 1
}
int main() {
A a;
a.x = 5;
inc(a);
printf("in main: %d\n", a.x); // => 5
A* b = new A;
b->x = 0;
inc(b);
printf("in main: %d\n", b->x); // => 1
return 0;
}
Output:
in inc: 6
in main: 5
in inc: 1
in main: 1
In C++, "pass by reference" means the function gets access to the original variable. It can assign a whole new literal integer and the original variable will then have that value too.
void replace(A &arg) {
A newA;
newA.x = 10;
arg = newA;
printf("in replace: %d\n", arg.x);
}
int main() {
A a;
a.x = 5;
replace(a);
printf("in main: %d\n", a.x);
return 0;
}
Output:
in replace: 10
in main: 10
Ruby uses pass by value (in the C++ sense) if the argument is not an object. But in Ruby everything is an object, so there really is no pass by value in the C++ sense in Ruby.
In Ruby, "pass by object reference" (to use Python's terminology) is used:
Inside the function, any of the object's members can have new values assigned to them and these changes will persist after the function returns.*
Inside the function, assigning a whole new object to the variable causes the variable to stop referencing the old object. But after the function returns, the original variable will still reference the old object.
Therefore Ruby does not use "pass by reference" in the C++ sense. If it did, then assigning a new object to a variable inside a function would cause the old object to be forgotten after the function returned.
class A
attr_accessor :x
end
def inc(arg)
arg.x += 1
puts arg.x
end
def replace(arg)
arg = A.new
arg.x = 3
puts arg.x
end
a = A.new
a.x = 1
puts a.x # 1
inc a # 2
puts a.x # 2
replace a # 3
puts a.x # 2
puts ''
def inc_var(arg)
arg += 1
puts arg
end
b = 1 # Even integers are objects in Ruby
puts b # 1
inc_var b # 2
puts b # 1
Output:
1
2
2
3
2
1
2
1
* This is why, in Ruby, if you want to modify an object inside a function but forget those changes when the function returns, then you must explicitly make a copy of the object before making your temporary changes to the copy.
Is Ruby pass by reference or by value?
Ruby is pass-by-value. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
Ruby is pass-by-value in a strict sense, BUT the values are references.
This could be called "pass-reference-by-value". This article has the best explanation I have read: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
Pass-reference-by-value could briefly be explained as follows:
A function receives a reference to (and will access) the same object in memory as used by the caller. However, it does not receive the box that the caller is storing this object in; as in pass-value-by-value, the function provides its own box and creates a new variable for itself.
The resulting behavior is actually a combination of the classical definitions of pass-by-reference and pass-by-value.
There are already some great answers, but I want to post the definition of a pair of authorities on the subject, but also hoping someone might explain what said authorities Matz (creator of Ruby) and David Flanagan meant in their excellent O'Reilly book, The Ruby Programming Language.
[from 3.8.1: Object References]
When you pass an object to a method in Ruby, it is an object reference that is passed to the method. It is not the object itself, and it is not a reference to the reference to the object. Another way to say this is that method arguments are passed by value rather than by reference, but that the values passed are object references.
Because object references are passed to methods, methods can use those references to modify the underlying object. These modifications are then visible when the method returns.
This all makes sense to me until that last paragraph, and especially that last sentence. This is at best misleading, and at worse confounding. How, in any way, could modifications to that passed-by-value reference change the underlying object?
Is Ruby pass by reference or by value?
Ruby is pass-by-reference. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar.object_id
end
baz = 'value'
puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"
=> 2279146940 Ruby is pass-by-reference 2279146940 because object_id's (memory addresses) are always the same ;)
def bar(babar)
babar.replace("reference")
end
bar(baz)
puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"
=> some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-reference
Parameters are a copy of the original reference. So, you can change values, but cannot change the original reference.
Try this:--
1.object_id
#=> 3
2.object_id
#=> 5
a = 1
#=> 1
a.object_id
#=> 3
b = 2
#=> 2
b.object_id
#=> 5
identifier a contains object_id 3 for value object 1 and identifier b contains object_id 5 for value object 2.
Now do this:--
a.object_id = 5
#=> error
a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2
a.object_id
#=> 5
Now, a and b both contain same object_id 5 which refers to value object 2.
So, Ruby variable contains object_ids to refer to value objects.
Doing following also gives error:--
c
#=> error
but doing this won't give error:--
5.object_id
#=> 11
c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11
a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true
a
#=> Value at a
#=> 11
Here identifier a returns value object 11 whose object id is 23 i.e. object_id 23 is at identifier a, Now we see an example by using method.
def foo(arg)
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
arg in foo is assigned with return value of x.
It clearly shows that argument is passed by value 11, and value 11 being itself an object has unique object id 23.
Now see this also:--
def foo(arg)
p arg
p arg.object_id
arg = 12
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23
Here, identifier arg first contains object_id 23 to refer 11 and after internal assignment with value object 12, it contains object_id 25. But it does not change value referenced by identifier x used in calling method.
Hence, Ruby is pass by value and Ruby variables do not contain values but do contain reference to value object.
It should be noted that you do not have to even use the "replace" method to change the value original value. If you assign one of the hash values for a hash, you are changing the original value.
def my_foo(a_hash)
a_hash["test"]="reference"
end;
hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Two references refer to same object as long as there is no reassignment.
Any updates in the same object won't make the references to new memory since it still is in same memory.
Here are few examples :
a = "first string"
b = a
b.upcase!
=> FIRST STRING
a
=> FIRST STRING
b = "second string"
a
=> FIRST STRING
hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}
def change(first_sub_hash)
first_sub_hash[:third_key] = "third_value"
end
change(first_sub_hash)
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
Ruby is interpreted. Variables are references to data, but not the data itself. This facilitates using the same variable for data of different types.
Assignment of lhs = rhs then copies the reference on the rhs, not the data. This differs in other languages, such as C, where assignment does a data copy to lhs from rhs.
So for the function call, the variable passed, say x, is indeed copied into a local variable in the function, but x is a reference. There will then be two copies of the reference, both referencing the same data. One will be in the caller, one in the function.
Assignment in the function would then copy a new reference to the function's version of x. After this the caller's version of x remains unchanged. It is still a reference to the original data.
In contrast, using the .replace method on x will cause ruby to do a data copy. If replace is used before any new assignments then indeed the caller will see the data change in its version also.
Similarly, as long as the original reference is in tact for the passed in variable, the instance variables will be the same that the caller sees. Within the framework of an object, the instance variables always have the most up to date reference values, whether those are provided by the caller or set in the function the class was passed in to.
The 'call by value' or 'call by reference' is muddled here because of confusion over '=' In compiled languages '=' is a data copy. Here in this interpreted language '=' is a reference copy. In the example you have the reference passed in followed by a reference copy though '=' that clobbers the original passed in reference, and then people talking about it as though '=' were a data copy.
To be consistent with definitions we must keep with '.replace' as it is a data copy. From the perspective of '.replace' we see that this is indeed pass by reference. Furthermore, if we walk through in the debugger, we see references being passed in, as variables are references.
However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.
Lots of great answers diving into the theory of how Ruby's "pass-reference-by-value" works. But I learn and understand everything much better by example. Hopefully, this will be helpful.
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar = "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----
As you can see when we entered the method, our bar was still pointing to the string "value". But then we assigned a string object "reference" to bar, which has a new object_id. In this case bar inside of foo, has a different scope, and whatever we passed inside the method, is no longer accessed by bar as we re-assigned it and point it to a new place in memory that holds String "reference".
Now consider this same method. The only difference is what with do inside the method
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar.replace "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----
Notice the difference? What we did here was: we modified the contents of the String object, that variable was pointing to. The scope of bar is still different inside of the method.
So be careful how you treat the variable passed into methods. And if you modify passed-in variables-in-place (gsub!, replace, etc), then indicate so in the name of the method with a bang !, like so "def foo!"
P.S.:
It's important to keep in mind that the "bar"s inside and outside of foo, are "different" "bar". Their scope is different. Inside the method, you could rename "bar" to "club" and the result would be the same.
I often see variables re-used inside and outside of methods, and while it's fine, it takes away from the readability of the code and is a code smell IMHO. I highly recommend not to do what I did in my example above :) and rather do this
def foo(fiz)
puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
fiz = "reference"
puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60
Yes but ....
Ruby passes a reference to an object and since everything in ruby is an object, then you could say it's pass by reference.
I don't agree with the postings here claiming it's pass by value, that seems like pedantic, symantic games to me.
However, in effect it "hides" the behaviour because most of the operations ruby provides "out of the box" - for example string operations, produce a copy of the object:
> astringobject = "lowercase"
> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase
> puts astringobject
lowercase
> puts bstringobject
LOWERCASE
This means that much of the time, the original object is left unchanged giving the appearance that ruby is "pass by value".
Of course when designing your own classes, an understanding of the details of this behaviour is important for both functional behaviour, memory efficiency and performance.

What does it mean that strings are "mutable" and why care?

I have been using Ruby on Rails for a while without studying Ruby, now I am taking Odin Project. I am not really sure about the answer of this question:
What does it mean that strings are "mutable" and why care?
Update:
so now I understand mutable string basically means the value in the memory can be changed after string is created.
immutable string means the value in the memory cannot be changed once created, only the reference can be changed.
based on the result of following code:
a = "foo"
a.object_id
=> 70218039369160
b = "bar"
a << b
=> "foobar"
a.object_id
=> 70218039369160
can I say string in Ruby is mutable? because the value in same memory is changed
a += b
=> "foobar"
a.object_id
=> 70218039184800
and the + method in Ruby actually create a new String object instead of change the value of the original String object, that's why the object id changed.
my question is will it cause any security problem if I use += and << interchangeably?
It means that you can modify an exising instance of a string, without constructing a new one. Consider the following code:
str1 = "foo"
str2 = str1
str1 += "bar"
In languages like javascript where strings are immutable, the value of str2 will still be "bar" after that code is executed, as you can see here. However, in languages where strings are mutable, like ruby, when you append "bar" at the end of str1 (using the ruby operator << which does that) you are actually modifying the instance, not creating a new one, so str2 will also be modified, as you can see here.
PS: Note that the append at the end of the string operator in ruby is << instead of += (+= actually creates a new string, but not because it's forced to do it, like in javascript).

Is it possible to see the ruby code in a proc?

p = Proc.new{ puts 'ok' }
Is is possible to see the ruby code in the proc?
inspect returns the memory location:
puts p.inspect
#<Proc:0x007f9e42980b88#(irb):2>
Ruby 1.9.3
Take a look at the sourcify gem:
proc { x + y }.to_source
# >> "proc { (x + y) }"
Do you mean the original source code or its bytecode representation ?
For the former you may use standard Proc's method source_location
p.source_location
=> ["test.rb", 21]
and read the appropriate lines of code.
For the latter it may come handy the RubyVM::InstructionSequence and its class method disassemble:
irb> RubyVM::InstructionSequence.disasm p
=> "== disasm: <RubyVM::InstructionSequence:block in irb_binding#(irb)>
=====\n== catch table\n| catch type: redo st: 0000 ed: 0011 sp: 0000
cont: 0000\n| catch type: next st: 0000 ed: 0011 sp: 0000 cont:
0011\n|------------------------------------------------------------------------\n
0000 trace 1
( 1)\n0002 putself \n0003 putstring \"ok\"\n0005
send :puts, 1, nil, 8, <ic:0>\n0011 leave \n"
No, there is no way to do that in Ruby.
Some Ruby implementations may or may not have implementation-specific ways of getting the source code.
You can also try to use Proc#source_location to find the file that the Proc was defined in, and then parse that file to find the source code. But that won't work if the Proc wasn't defined in a file (e.g. if it was defined dynamically with eval) or if the source file no longer exists, e.g. because you are running an AOT-compiled version of your program.
So, the short answer is: no, there is no way.
The long answer is: there are some ways that may or may not sometimes work depending on way too many factors to even begin to make this work reliably.
That's not even taking into account Procs which don't even have a Ruby source code because they were defined in native code.
If proc is defined into a file, U can get the file location of proc then serialize it, then after deserialize use the location to get back to the proc again
proc_location_array = proc.source_location
after deserialize:
file_name = proc_location_array[0]
line_number = proc_location_array[1]
proc_line_code = IO.readlines(file_name)[line_number - 1]
proc_hash_string = proc_line_code[proc_line_code.index("{")..proc_line_code.length]
proc = eval("lambda #{proc_hash_string}")
Although an old question, still, I wanted to share my thoughts.
You can use Pry gem and end up with something like this:
[11] pry> p = Proc.new{ puts 'ok' }
=> #<Proc:0x007febe00e6360#(pry):23>
[12] pry> show-source p
From: (pry)
Number of lines: 1
p = Proc.new{ puts 'ok' }
Also, if you would use it from Rails context, you can put:
::Kernel.binding.pry
in your controllers or models, and
- require 'pry'; binding.pry
in your views, where you want to start debugging.
And in the tests, I use a combination, first require 'pry' at the top, and then ::Kernel.binding.pry where needed.
References:
http://pryrepl.org
https://github.com/pry/pry
https://github.com/pry/pry/wiki/Source-browsing

Why don't numbers support .dup?

>> a = 5
=> 5
>> b = "hello, world!"
=> "hello, world!"
>> b.dup
=> "hello, world!"
>> a.dup
TypeError: can't dup Fixnum
from (irb):4:in `dup'
from (irb):4
I understand that Ruby will make a copy every time you assign an integer to a new variable, but why does Numeric#dup raise an error?
Wouldn't this break abstraction, since all objects should be expected to respond to .dup properly?
Rewriting the dup method will fix the problem, as far as I can tell:
>> class Numeric
>> def dup()
>> self
>> end
>> end
Does this have a downside I'm not seeing? Why isn't this built into Ruby?
Most objects in Ruby are passed by reference and can be dupped. Eg:
s = "Hello"
t = s # s & t reference to the same string
t.upcase! # modifying either one will affect the other
s # ==> "HELLO"
A few objects in Ruby are immediate, though. They are passed by value, there can only be one of this value and it therefore cannot be duped. These are any (small) integers, true, false, symbols and nil. Many floats are also immediates in Ruby 2.0 on 64 bit systems.
In this (preposterous) example, any "42" will hold the same instance variable.
class Fixnum
attr_accessor :name
alias_method :original_to_s, :to_s
def to_s
name || original_to_s
end
end
42.name = "The Answer"
puts *41..43 # => 41, The Answer, 43
Since you would normally expect something.dup.name = "new name" to not affect any other object than the copy obtained with dup, Ruby chooses not to define dup on immediates.
Your question is more complex than it appears. There was some discussion on ruby-core as to how this can be made easier. Also, other types of Numeric objects (floats, bignums, rationals and complex numbers) can not be duped although they are not immediates either.
Note that ActiveSupport (part of rails) provide the method duplicable? on all objects
The problem with the dup() function that you defined is that it doesn't return a copy of the object, but rather returns the object itself. This is not what a duplicate procedure is supposed to do.
I don't know Ruby, but a possible reason I can think of for dup not being defined for numbers is that a number is a basic type and thus, doing something like:
>> a = 5
>> b = a
would automatically assign the value 5 into the variable b, as opposed to making b and a point to the same value in memory.

Resources