The other day, a colleague and I were digging through some code and ran across something that blew his mind. The code in question didn't seem that strange to me. That's when I realized that we had entered... The Twilight Zone.

I don't want to paste any real live code in here, so I'll give you the Cliff's notes:

public void ApplyAuthorization(Request request)
{
    request.Headers.Add(authorizationHeader);
}

Now, I'll be the first to say that this design is a little bit strange compared with my usual stuff. I'll normally try to keep assignments in plain view, and to avoid side effects in most cases, but I just saw no reason to beat around the bush here. What we're doing is taking an ordinary HTTP request and adding an authorization header. (Although, to be clear, the interface for this request object is made up.)

What makes this design useful is that this function is a part of the interface for the client object, and so the behavior of this function can vary from one implementation to another. Because this function returns nothing and imposes no expectations upon the caller, we can safely do anything or nothing within it. Cool, right?

So, even though side effects are practically a religious faux pas these days, I think this is a defensible decision, and this kind of code is not uncommon. In fact, this is a pattern younger programmers learn fairly early on. Unfortunately, it does not always work this way.

Sometimes things get weird

Below, you will find a standard issue C# enum.

enum Direction
{
    North,
    East,
    South,
    West,
}

Under the hood, you may be aware that this is, in fact, nothing more than an integer. This means that enums, though not labeled with the struct keyword, are in fact value types. This means that, rather than being passed to functions as references, they are passed by value. Exactly what this means is sometimes not well understood, even by competent C# developers.

What does it mean?

Well, I spoke to another colleague to get his thoughts on it. He said that they are "read only." Effectively, he's kind of right. You cannot modify the value of Direction.North in the same sense as you cannot modify the value of 3. Three is three is always three, no matter how much you may hate that number, and North is North, even if it's full of damn Yankee scum.

Still, that's not quite what he meant. Mostly, what he meant is that you cannot modify such a value within the context of a given function and expect that change to be reflected elsewhere. That's also kind of true. Just... You know. Only kind of.

What it really means is that when you define a variable var direction = Direction.North, you cannot modify the value of direction by passing direction to a function because you cannot pass direction to a function at all.

Bullshit, of course you can

No, you can't. I'm serious. Watch:

var direction = Direction.North;  // Create direction variable
CycleDirection(direction);        // Copy direction variable for function call

See? Nothing whatsoever happened with the original variable. It now has a clone, and we're messing with the value of the clone. That's what brought up this subject in the first place: a question on /r/learnprogramming about why the above design didn't work.

So can you do this with value types or not?

If this were Java, that would mean that God hates you, and the answer would be, "No, Virginia, there is no Santa Claus." Fuck Java.

bad-santa

For this and a pile of other reasons, I write in more reasonable languages. Here's how you do it in C#:

public void Main()
{
    var direction = Direction.North;
    CycleDirection(ref direction);
    Console.WriteLine(direction);     // Prints "South"
}

public void CycleDirection(ref Direction direction)
{
    // Use far superior direction
    direction = Direction.South;
}

Basically, the ref keyword causes us to pass a reference to direction even though it is a value type, thereby avoiding the copying behavior I mentioned above and allowing us to modify the original value. In a sense. Three is still three, but the number stored at memory address 0x0123screwhex22 is now Direction.South.

In short, it works exactly as you would expect if you are already a card-carrying member of the club. Unfortunately, there is very little to signal to newer programmers that this whole ref shenanigan is necessary, because this "just works" for reference types. Possibly the most confusing thing on earth is the fact that DateTime is not a reference type and, therefore, is subject to this kind of crap, same as integers and enums.

There are other languages where this works a little bit better. See below for details (code also available on the rust playground):

fn main() {
    let mut direction = Direction::South;
    cycle_direction(&mut direction);
    println!("{:?}", direction);
}

fn cycle_direction(direction: &mut Direction) {
    use Direction::*;
    
    *direction = match *direction {
        North   =>  East,
        East    =>  South,
        South   =>  West,
        West    =>  North,
    }
}

&mut Direction describes a unique reference to a Direction and allows modification. Because (explicitly!) we do this by a reference, the original value is changed, which allows the change to be reflected in the calling context. Very simple. Very direct. But I have an experiment for you.

Try removing &mut from the function parameter. You would also need to remove it from the call site, of course, but that's no big deal. Give it a shot.

...What's that? Still doesn't compile? You're right. But not just because you forgot to remove the two derefs from the body of cycle_direction on line 18. The more important reason is that, after moving the value direction into the cycle function, you later reference that value in the calling context as if it were still there.

This is a no-no. More importantly, though, it's the kind of no-no that stops new programmers from getting confused by weirdly arbitrary rules. I kind of like it. What do you think?