Arrays are passed by value, no by reference, no by value…

One thing that ColdFusion has always done in a relatively odd way was, it tended to pass Structs by reference, and arrays by value.

This seems to be a decision that was made back in the hey-day of the product, and has been continued since then to maintain backwards compatibility.

But I started noting something that I was doing inside my CFCs, that lead me to believe (and realise) that arrays are only passed by value sometimes.

This came to pass when I would often have a getter and a setter method for an array inside a CFC, and would often perform operations on it inside the CFC instance like so:

<cfscript>
 ArrayAppend(getThingArray(), newThing);
</cfscript>

and had never really thought anything about it.

However, if you look at it, if getThingArray() (which returns an array), it shouldn’t actually change the state of the object if I call ArrayAppend() on it, as it should have returned the array by value.

That being said, it does change the value, so we can see here in this case, it does return the array by reference, not by value.

Some more investigation reveals that an array is passed by reference only when the array is returned inside a ColdFusion native function, such as ArrayAppend() or ArrayDeleteAt().

This has some interesting consequences for encapsulation that you may need to be aware of, for example, doing:

<cfscript>
 obj = createObject("component", "obj").init();
 array = obj.getArray();
 ArrayAppend(array, "george");
</cfscript>

Will only ever result in the external array being changed, the state of ‘obj’ will never be altered, however if we:

<cfscript>
 obj = createObject("component", "obj").init();
 arrayAppend(obj.getArray(), "fred");
</cfscript>

This will mean that the state of ‘obj’ will be changed, and ‘fred’ will be appended to the array stored inside of ‘obj’.

Depending on how strong you want your encapsulation to be, this could be an issue for you.  The same would apply for structs as they are always passed on by reference, however it seems to be a little less obvious when applied to arrays.

This was just something I picked up on the other day and thought I would share it, in case it tripped someone up along the way.

Leave a Comment

Comments

  • wolf2k5 | November 3, 2005

    It looks definitely a bug to me, it should behave consistently …

    What version of ColdFusion are you using?

    Thanks.

  • Gareth Edwards | November 3, 2005

    Aparently there is a bug in Coldfusion 6 that they "fixed" in coldfusion 7 that means that the object passed is actually by reference via the arguments scope. Ie arguments were by reference in some cases there for it looked like some array’s passed to functions were as well. *shrugs*

  • Brian Kotek | November 4, 2005

    That IS very bizarre! I’m happy there is actually a way to get the array by reference, but disappointed that there is yet another special case that must be remembered…:-(

  • Jeff Houser | November 4, 2005

    I just tested this in 7.01 and was able to replicate the behavior described.

    However, in the problem example, you’re passing an object into the function, not an array. Objects are passed by reference. Based on the results, I would suspect that CF parses the method call inside the function, as opposed to parsing it first and then sending the results.

    It’s a bit odd / confusing no matter how you look at it. I wish CF had the ability to specify whether an argument was passed by reference or value.

    BlueDragon confuses the issue even more, appearing to pass arrays by reference.

  • Mark | November 4, 2005

    Jeff:
    I’m sorry, but I’m not seeing where I am passing in an object? do you mean:

    arrayAppend(obj.getArray(), "fred");

    ? Because in this case, I have passed in the array, in the post I specify that getArray() returns an array.

    Some further testing also reveals that if you call a Java method on the array, it is also passed by reference, i.e.
    obj.getArray().add("new thing"); will also change the state of obj.

    However, if you passed the array to a UDF that calls arrayAppend() or related, it is passed by value. So customAddThing(obj.getArray())

    function(array) { arrayAppend(arguments.array, "new value"); }

    will NOT change the state of obj.

    Oh yeah, this was tested on 7.0, I should also run some tests on 6.1 to see what happens.

  • Mark | November 4, 2005

    I just tested this further on 6.1 and the same thing occurs – however in the case when you pass an array to a function, customAddThing(obj.getArray()) it WILL change the state of the object, thus it is passed by reference.

    — the function above should be function customAddThing(array).

    Fun, no?

  • Sean Corfield | November 5, 2005

    Assignment causes the copy of the array. When you return an array, you haven’t actually assigned it so it is still a reference. As soon as you assign that to a variable, you create a copy.

    For the argument passing case, it looks like 6.1 had a bug that did NOT treat argument passing as assignment for arrays. CFMX 7.0 has fixed that.

    So, really, it *is* completely consistent but it’s just not very intuitive! 🙂