So this is something I've blogged about before, but as it recently burned a friend of mine, and got some pushback from folks on Twitter, I thought I'd raise the issue again.
When working with ColdFusion, any complex data type (structs, CFCs, queries, COM/JavaObjects, did I miss one?) will be passed by reference, not by value.
For those of you who took English Lit 1 instead of Comp Sci 101, all this means is that these variables are not copied when sent to a function. In other words, when I do:
<cfset newThing = changeIt(oldThing)>
Under normal circumstances, if changeIt manipulates values in oldThing and does a return on it, my newThing will have the manipulated values and oldThing will be just fine.
But with complex data, like structures, you are actually passing the exact same object (a reference). That means any changes inside the UDF will be reflected on the original object.
As an example:
<cfscript>
function fubar(data) {
arguments.data.name = "Britney";
return arguments.data;
}
</cfscript>
<cfset s = {}>
<cfset s.name = "Paris">
<cfset s2 = fubar(s)>
<cfdump var="#s#" label="S">
<cfdump var="#s2#" label="S2">
I've created a simple structure with one key. I've created a UDF that changes the value and returns a 'new' struct (right?). But look at what the dumps show:

Fail! Luckily enough it is easy to fix:
<cfset s2 = fubar(duplicate(s))>
The duplicate function will create a deep copy of the structure before sending it to the UDF.
Now, here is where things get interesting. Did you notice I didn't mention arrays in my list of complex objects? Well, they are indeed copied by value, not by reference, but, if you include a structure as part of the array, the structure portion will be copied by reference. Check this out:
<cfscript>
function fubar2(data) {
arguments.data[1] = 9;
arguments.data[2].gender = "female";
arguments.data[3] = "Not so goofy";
return arguments.data;
}
</cfscript>
<cfset a = []>
<cfset a[1] = "42">
<cfset a[2] = {}>
<cfset a[2].gender = "male">
<cfset a[3] = "Goofy">
<cfset b = fubar2(a)>
<cfdump var="#a#" label="A">
<cfdump var="#b#" label="B">
I've got an array with 3 values. Values 1 and 3 are strings, value 2 is a structure. My new UDF, fubar2, changes all 3 values. And the result?

As you can see, the simple values weren't changed in the original, but the embedded structure was. As before, using a duplicate around 'a' would solve the problem.
But is it a problem? Well certainly if you aren't expecting it, but the fact that structs and other complex objects pass by reference isn't "wrong" per se, and it can be useful as well. Just don't forget it.
Archived Comments
I guess you missed arrays :)
Eh?
You asked if you missed any complex data types :) An array is, although in CF arrays are passed by value not by reference.
I find passing ararys by value as something that shoudl be solved. Adobe could think to a checkbox in admin for keeping back compatibility but passing arrays by value looks like a bug to be solved and not like a language tipical aspect to be preserved.
This is just a buyer beware type of thing and every language has its own little gotchas. This is important to know but I'm not sure that changing it after 8 releases would be a good thing. Just my opinion as someone who has had legacy code broken for the sake of satisfying someone's newly found ideology. I'm sure that there are probably strong arguments to be made either way.
@Andy
I do nto think is only ideology.
Passing by value means that any time you pass an array a new one is made.
This is one thing but cf is full of these littles tricks that make it run slower than railo for example..
@Justin: Oh, well, I thought Rolando meant I missed talking about arrays, and that was my second area. Yes, they are complex, I just wanted to make sure to separate them from the 'expected' list of things passed by ref.
Yes, I'm being defensive. Sue me. ;)
Ray and I had this discussion while he was writing this blog about whether or not this should be fixed in future versions, it's valid that backward compatibility issue would be at play here but this is a inconsistency nonetheless. I am glad however that he posted this gotcha so at least people are aware of the implications and won't go crazy if they see this odd behavior.
cheers..
@Ray: Yeah of course, the most important part of your article is the distinction that arrays aren't passed by reference like other complex types and need to be thought of separately :)
Ray; I love you man but your example isn't very good; it's what I'd expect. This gives you the full "WTF?!?!" effect:
<cfset s = {} />
<cfset s.name = "Britney">
<cfset s2 = s />
<cfdump var="#s#" label="S">
<cfdump var="#s2#" label="S2">
<cfset s2.name = "Paris">
<cfdump var="#s#" label="S">
<cfdump var="#s2#" label="S2">
Result: All Paris all the time!
Ben Nadel posted a 'sweet' coldfusion pass array by reference, that has a caveat perhaps...
http://www.bennadel.com/blo...
Passing arrays by value = FAIL
I agree above, arrays are passed by value incorrectly. Took me a while to figure out that Adobe dropped the ball here.
Its interesting to me that structs are passed by reference. C passes structs as value but I guess since Coldfusion is Java structs is just another object and so passed by reference.
"When working with ColdFusion, any complex data type (structs, CFCs, queries, COM/JavaObjects, did I miss one?) will be passed by reference, not by value."
This is technically incorrect. The reference to the object is actually passed by value. Most of the time you don't have to worry about it, but imagine you had something like this:
public void function test() {
var s = {
"name" = "James"
};
refTest(s);
writeDump(var:s, abort:true);
}
private void function refTest(struct t) {
t = {};
}
You might assume that it will dump out an empty struct when it actually dumps out the unmodified original struct. This is because the value of s (the reference to the struct) is passing a copy to refTest.
Interesting - yet my other examples do show this behavior. So am I wrong - or just not specifying this right?
The other examples work because the reference points to the same object.
So in my example above - refTest(struct t) - t contains a copy of the reference to struct s. If I was to do:
t.name = "Ray";
The dump would now show
name = "Ray"
This is, as you state in your post, because refTest used the reference to alter the underlining struct. However, if (like I did originally) change t to point to a new struct hence a new reference - this doesn't affect the original reference in my caller function. s still points to the same struct. This is because the reference was passed by value. In other words a copy of the reference was created and passed to the refTest function.
Interesting. But wouldn't you say - in a typical case - when you pass a struct to a method the method is normally going to do something with it. Like add or remove a key? And in most cases, the issue of it changing the original would possibly hurt people?
Yeah sure. For the most part - I don't imagine most people would run into this. I was just pointing out that whilst in general most people would think of passing a struct is passing by reference - technically it's not.
A colleague of mine actually come up against this very issue a few months ago. He was passing a structure into a function which used the values contained in the struct and then he wanted to "clear" it by creating a new structure. Bit of a shock when in the caller function - the structure still contained all the original values!
Maybe I'm being picky - but how is it _not_ passing by reference? It seems like you are saying that when you made T a new struct, the reference broke. But it was still a reference at first. So it was -passed- in as a reference, but then the connection was lost. Right?
The formal definition of pass by reference is actually quite complex but the important thing to note is that if you pass a variable by reference, the method you're calling can change the value of the caller's variable by changing its parameter value.
As you see from my example - if I change the parameter value (t) it doesn't change the callers variable (s). In other words, if I change t to a new reference - s still stays the same. So by it's formal definition - this is not pass by reference.
From a abstract view - you can think of this as passing by reference - because the argument to refTest is a reference. But the point I was trying to make is by the correct definition of pass by reference, passing a struct or any complex type is still pass by value - we just happen to be passing a reference.
It's probably me just being pedantic, however I think it's a distinction that people need to be aware of.
I concede your point - I just think it's going to _act_ more like the first case then the second. But I see your point.