Hacker News new | ask | show | jobs
by tialaramex 703 days ago
It's probably mean for me to say "empty type" to C++ people because of course just as std::move doesn't move likewise std::is_empty doesn't detect empty types. It can't because C++ doesn't have any.

You may need to sit down. An empty type has no values. Not one value, like the unit type which C++ makes a poor job of as you explain, but no values. None at all.

Because it has no values we will never be called upon to store one, we can't call functions which take one as a parameter, operations whose result is an empty type must diverge (ie control flow escapes, we never get to use the value because there isn't one). Code paths which are predicated on the value of an empty type are dead and can be pruned. And so on.

Rust uses this all over the place. C++ can't express it.

3 comments

Help me out here.

What is this empty type for? Could you provide an old man with a nice concrete example of this in action? I've used empty types in C++ to mark the end of recursive templates - which I used implement typelists before variadic templates were available.

But then you mention being unable to call functions which take an empty type as a parameter. At which point I cease to understand the purpose.

I don't know that I'll be able to convince you but I'll give a couple of examples.

What is the type of the expression "return x" ? Rust says that's ! pronounced Never, an empty type. This expression never had a value, control flow diverges.

So this means we can just use simple type arithmetic to decide that a branch which returns contributed nothing to the type of the expression - it has no possible value. This wasn't a special case, it's just type arithmetic.

Ok, lets introduce another. Rust has a suite of conversion traits. From, Into, TryFrom and TryInto. They're chained, so if I implement From<Goose> for Doodad, everybody gets the three other implied conversions. But the Try conversions are potentially fallible, hence the word Try. So they have an error type. Generic Code handling the Error type of potentially failing conversion will thus be written, even if in some cases the conversion undertaken chained back to my From<Goose> code. But wait, that conversation can't fail! Sure enough the chained TryFrom and TryInto produced will have the error type Infallible, which is an Empty Type.

So the compiler can trim all the error handling code, it depends upon this value which we know can't exist, therefore it never executes.

Thanks for the clarification.
void is an empty type in C++. It's less useful than it could be, but it does exist.
void isn't a type. If you try to use it as a type you'll be told "incomplete type".

People who want void to be a type in C++ (proponents of "regular void") mostly want it to be a unit type. If they're really ambitious they want it to have zero size. Generally a few committee meetings will knock that out of them.