"Tookelso" <tookskie-googlegroups / yahoo.com> writes:

> Hello,
> 
> Sorry for asking this basic question, but I couldn't find it in the
> PickAxe book.
> 
> When you pass an argument to a method, is it passed by reference, or by
> value?

By value.  But remember that the value of a variable is always a
reference!  (That's not *technically* true for Fixnums, but since
Fixnums are entirely immutable, the abstraction holds up.)

> Or, does it depend on the object's type?
> 
> For example, in the simple script below, the program outputs "0".
> #-----------------------------------
> def test(c_count)
> c_count = c_count + 1
> end
> # begin program
> c_count = 0
> test(c_count)
> puts c_count
> #-----------------------------------
> I realize that for simple things, it's better to return a value from
> the method, but I would like to know *why* it's not modifying my
> "c_count" value.

(In this explanation, I'm ignoring the fact that Fixnums are special --
 that fact is a pure implementation detail.  Just an optimization.)

When you initialize c_count, the 0 is evaluated first.  Ruby will
allocate space for a Fixnum.  Let's say it allocates some bytes over at
the memory location 0xCAFEBABE.  It copies 0x0000, or whatever the
machine representation of 0 is, to 0xCAFEBABE.

Then it creates a new instance variable.  Sets its value to 0xCAFEBABE.

When you call ``test'', c_count is passed by value, as always.  The
value passed is 0xCAFEBABE.

Now, what happens when you receive an argument is this: a new instance
variable is created.  This c_count has nothing in common with the other
c_count -- except that they happen to share values.  Of course, you
could name the test parameter ``snuggly_taco'', and the program would
work the same.

When you assign to snuggly_taco, the only thing that happens is that the
value of snuggly_taco is changed, to the address of that new number
you're making.  This affects neither 0xCAFEBABE nor c_count.

If you really want to change the value of an instance variable from
another function -- bad luck.  You can't.

Actually,  I lied.  You can do it.  If you use Florian Gros
binding_of_caller and eval.

But that's an extremely obscure hack.  Avoid it!  The clean solution is,
as always, adding another layer of indirection.  Make it so that by
setting something you actually can change -- like an instance variable
-- you change the effective value.

| class Box
|   attr_accessor :value
| 
|   def initialize value=nil
|     @value = value
|   end
| end
| 
| def test c_count
|   c_count.value = c_count.value + 1
| end
| 
| c_count = Box.new 0
| test c_count
| puts c_count

This box is perfectly analogous with a C pointer (except, of course,
that you can't do pointer arithmetic).  Instead of ``*foo'', we write
``foo.value''.

(By the way, is there a class like this in the stdlib?)

mikael