Question

Why does

if (x) {
    f();
}

call f() if x is an empty string ""?

Shouldn't empty strings in D implicitly convert to bool false like they do in Python and when empty arrays does it (in D)?

Update: I fixed the question. I had incorrectly reversed the reasoning logic. Luckily, the bright D minds understood what I meant anyway ;)

Was it helpful?

Solution

Conditions and if statements and loops are cast to bool by the compiler. So,

if(x) {...}

becomes

if(cast(bool)x) {...}

and in the case of arrays, casting to bool is equivalent to testing whether its ptr property is not null. So, it becomes

if(x.ptr !is null) {...}

In the case of arrays, this is actually a really bad test, because null arrays are considered to be the same as empty arrays. So, in most cases, you don't care whether an array is null or not. An array is essentially a struct that looks like

struct Array(T)
{
    T* ptr;
    size_t length;
}

The == operator will check whether all of the elements referred to by ptr are equal, but if length is 0 for both arrays, it doesn't care what the value of ptr is. That means that "" and null are equal (as are [] and null). However, the is operator explicitly checks the ptr properties for equality, so "" and null won't be the same according to the is operator, and whether a particular array which is empty has a null ptr depends on how its value was set. So, the fact that an array is empty really says nothing about whether it's null or not. You have to check with the is operator to know for sure.

The result of all this is that it's generally bad practice to put an array (or string) directly in a condition like you're doing with

if(x) {...}

Rather, you should be clear about what you're checking. Do you care whether it's empty? In that case, you should check either

if(x.empty) {...}

or

if(x.length == 0} {...}

Or do you really care that it's null? In that case, use the is operator:

if(x is null) {...}

The behavior of arrays in conditions is consistent with the rest of the language (e.g. pointer and reference types are checked to see whether they're null or not), but unfortunately, in practice, such behavior for arrays is quite bug-prone. So, I'd advise that you just don't ever put an array by itself in the condition of an if statement or loop.

OTHER TIPS

the default conversion of arrays looks at the .ptr, which means only the default initialized arrays (or explicitly set to null) evaluate to false

as an added effect string literals in D are \0 terminated which means ("")[0] == '\0' and as such ("").ptr can't be null (which would lead to a segfault)

IMO it should look at the length and you can use the ptr when you need to

It does when I try it...

void main() {
    import std.stdio;
    string s = "";
    if(s)
            writeln("true"); // triggered
}

If it was "string s = null;" (which is the default initialization), it doesn't, because the null converts to false, but "" is ok on my computer. Are you sure it isn't null?

BTW, if you want to test for (non-)emptiness, the way I prefer to do it is if(x.length) and if(x.length == 0). Those work consistently for both "" and null, then if you specifically want null, do if(x is null). It is just a little more clear, especially since "" and null are interchangeable in a lot of other contexts in D.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top