ColdFusion: StructCopy & Magic Structs

Quick post about a Coldfusion oddity I came across this week, whilst attempting to use CF's built in StructCopy function.

Coldfusion has two primary mechanisms to clone a Struct (that's a map to Java folk): StructCopy() and Duplicate().  StructCopy is a shallow copy, where as Duplicate is a full blown deep copy - so if you are attempting to clone a complex nested struct then duplicate is probably the function for you (although, beware, as you may expect it comes with some performance penalty!).

What I actually wanted to do was clone the URL scope (which is for all intents and purposes is just a Struct of key/value pairs of query string params in the URL).  I just wanted to clone the current URL scope struct of query params so I could alter the struct (add additional params etc) without actually affecting the URL scope (normal FP type stuff). As the URL scope is always just going to be a struct of String key/value pairs (Strings being immutable), I figured the shallow copy StructCopy function would do (the struct would always be a simple single level struct, and all key/values would be Strings - so any changes to them would not affect the original URL scope).

Oh no. It doesn't work.

To be fair to CF, the URL scope isn't a straight forward Struct - it is actually a Coldfusion URLScope object - but just masquerades as a Struct most of the time, writeDump()'ing it labels it as a Struct, passing it to a function that requires a Struct argument - no probs.

Here is some example code:

In the above scenario, after the structDelete - both Structs output as the same thing. The key "rob" has been removed from both Structs. Well actually, just the one. But actually the cloned struct isn't cloned at all, it's just the original URL scope again.

To me, that sucks. Really.

Like I said, I get that URL is not really a true struct, so I don't blame CF for not wanting to play nicely (although duplicate( url ) will work), but returning the URL structure? not cool.

There are a few options that I see can happen if CF doesn't want to StructCopy:
  • Throw an exception. To me, this is the best option. Everyone knows where we are, and really it is an exception - if we are saying truly, that StructCopy cannot copy a URLScope object, then its an exception.
  • Return an empty struct - not ideal, but again, forces handling of this potential - kind of StructCopy saying, look guys, I tried to copy but failed, so here's an empty struct.

In no circumstance is it cool to just silently return the URLScope object.  Appearing to be working correctly (you get a what looks like a struct back, with all the same keys/values as your original struct, so all good right?) but actually providing the exact opposite functionality you actually want. Dangerous.

I was lucky that the code I was writing flagged up the errors immediately in an obvious fashion - but if you are cloning just to avoid an unusual edge-case, you may be in for an unpleasant suprise.