Posted in ColdFusion | Posted on 05-01-2009 | 5,032 views
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:
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:
2function fubar(data) {
3 arguments.data.name = "Britney";
4 return arguments.data;
5}
6</cfscript>
7
8<cfset s = {}>
9<cfset s.name = "Paris">
10<cfset s2 = fubar(s)>
11<cfdump var="#s#" label="S">
12<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:
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:
2function fubar2(data) {
3 arguments.data[1] = 9;
4 arguments.data[2].gender = "female";
5 arguments.data[3] = "Not so goofy";
6 return arguments.data;
7}
8</cfscript>
9
10<cfset a = []>
11<cfset a[1] = "42">
12<cfset a[2] = {}>
13<cfset a[2].gender = "male">
14<cfset a[3] = "Goofy">
15<cfset b = fubar2(a)>
16<cfdump var="#a#" label="A">
17<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.


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..
Yes, I'm being defensive. Sue me. ;)
cheers..
<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!
http://www.bennadel.com/blog/275-Passing-Arrays-By...
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.
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.
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!
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.
[Add Comment] [Subscribe to Comments]