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:
Fail! Luckily enough it is easy to fix:
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.
1 <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:
1 <cfscript>
2 function 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:
2 function 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">
Fail! Luckily enough it is easy to fix:
1 <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:
1 <cfscript>
2 function 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?
2 function 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">
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.
Comment 1 written by Rolando Lopez on 1 May 2009, at 10:23 AM
Comment 2 written by Raymond Camden on 1 May 2009, at 10:24 AM
Comment 3 written by Justin Carter on 1 May 2009, at 11:05 AM
Comment 4 written by Andrea on 1 May 2009, at 11:08 AM
Comment 5 written by Andy Sandefer on 1 May 2009, at 11:33 AM
Comment 6 written by Andrea on 1 May 2009, at 11:41 AM
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..
Comment 7 written by Raymond Camden on 1 May 2009, at 12:55 PM
Yes, I'm being defensive. Sue me. ;)
Comment 8 written by phill.nacelli on 1 May 2009, at 1:51 PM
cheers..
Comment 9 written by Justin Carter on 2 May 2009, at 7:21 AM
Comment 10 written by Jim_Collins on 2 May 2009, at 11:07 PM
<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!
Comment 11 written by Chuck Savage on 7 December 2009, at 11:26 AM
http://www.bennadel.com/blog/275-Passing-Arrays-By...
[Add Comment] [Subscribe to Comments]