To understand this, you have to know what an IEnumerator actually is. As @BenZed said, Unity didn’t invent them, and in fact, their usage in Unity is really, really bizarre and kind of a hack. Let’s start with a more accurate definition of a coroutine. You probably think of it something like: “A function that can take multiple frames to execute”. But think about that, and then remember that coroutines exist outside Unity. There’s no such thing as a “frame”, inherently, outside of Unity. So what is a coroutine, really?
A normal function does some stuff, and then returns a value, and then it’s done. A coroutine does some stuff, returns a value, but is not necessarily done. Instead, it “yields”. When it returns, it sends control back to whatever called it; it’s saying “Hey, I’m done with the thing I did; I have more stuff to do, though, so I’ll just be waiting here for you to call me.”
So here’s the problem. When you just call “yield return SubRoutine();”, you never do anything else with that. The coroutine yield returns, and then it’s just gone. It’s like a line of code that looks like:
Vector3.Distance(Vector3.zero, transform.position);
Sure, this is a valid function call, but it spits out a number and you’re not waiting there with a variable in which to catch it, so it just vanishes. And, like your example, it fails silently.
That’s where StartCoroutine comes in. StartCoroutine grabs that yielded coroutine and stashes it in Unity’s internals. Unity, once a frame, crawls through this stash and resumes each function that was waiting. Unity has to go out of its way to make this happen; it doesn’t happen automatically, just because a coroutine exists.