March 31, 2023
On Fri, Mar 31, 2023 at 11:20:41AM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]
> But we have such a thing, in the `default:` case requirement.
> 
> In my code, it happens quite a bit that I throw an exception in the default case, or return something, but there's still a lot of `default: break;` code in there.
[...]
> How much does this rule help you, vs. annoy you?
[...]

It annoys me. It's verbose and ugly.  But OTOH, I'm also thankful for it, because it has actually helped me catch bugs in my code on a few occasions -- when I forgot to write the default case.

So I dunno, I'm on the fence about this.  Maybe if there was a less verbose way to indicate that the missing case was intentional?


T

-- 
Written on the window of a clothing store: No shirt, no shoes, no service.
March 31, 2023
On 3/31/23 17:20, Steven Schveighoffer wrote:
> Imagine if every `if` statement required you to write an `else` clause, even if it was empty. The idea would be, that you have to affirmatively attest that you didn't want to have a special case for when it didn't match the condition.
> 
> How fast do you think that everyone would run screaming for the exits?
> 
> But we have such a thing, in the `default:` case requirement.
> 
> In my code, it happens quite a bit that I throw an exception in the default case, or return something, but there's still a lot of `default: break;` code in there.
> 
> But consider a few things here:
> 
> 1. I have `final switch` at my disposal whenever I need it, which allows me to avoid `default:` and is really meant for "I want to make sure I handle everything"
> 2. If you forget to return something, or throw an exception, the compiler will still let you know (i.e. some code paths don't return a value). This counts for the majority of places where I have a `default` case that's not just `break;`.
> 3. I know in my experience (yours may vary) that when I forget to include a default, it's usually because I forgot to include `default: break;`. So the rule hasn't really helped me avoid bad code.
> 4. `switch` is already bloated enough with the requirement for `break` or `goto` between cases.
> 
> If we wanted to relax this requirement it's actually an easy change -- no currently-compiling code will break.
> 
> Why not? How much does this rule help you, vs. annoy you?
> 
> -Steve

I prefer the current behavior.
March 31, 2023
On 3/31/23 2:40 PM, Sebastiaan Koppe wrote:
> On Friday, 31 March 2023 at 18:30:27 UTC, Steven Schveighoffer wrote:
>> Do you feel the same about if statements without `else`? Why is it different?
>>
> 
> If statements are rather binary, and it is easy to understand the two paths.
> 
> Switches often have more than 2 cases, and its easier to forget one.

Complex if conditions are not "binary". I find it just as easy to forget a clause in an if statement that does the right thing.

> 
> Also consider refactoring. With switches I might add or rename or replace one of the values, which doesn't happen with if statements.

If you "forget a case statement", or refactor it, forcing you to write `default: break;` doesn't fix that.

-Steve
March 31, 2023
On Friday, 31 March 2023 at 21:04:52 UTC, Timon Gehr wrote:
> I prefer the current behavior.

Me too.
If you don't like default, use enums and final switch.
If you use switch over intergers, very often some values are not covered with cases, so even if you don't expect them to occur, it can happen. Therefore having a default case (that e.g. throws) is useful most of the time.
March 31, 2023
On Friday, 31 March 2023 at 21:24:25 UTC, Steven Schveighoffer wrote:
> If you "forget a case statement", or refactor it, forcing you to write `default: break;` doesn't fix that.
>

Whenever I do write then, I do it reluctantly and mostly as a last resort.

Having the default be open-endedness means I don't get reminded about that ugly detail anymore.

Of the two evils, I prefer the explicit one.
March 31, 2023

On 3/31/23 6:14 PM, Sebastiaan Koppe wrote:

>

On Friday, 31 March 2023 at 21:24:25 UTC, Steven Schveighoffer wrote:

>

If you "forget a case statement", or refactor it, forcing you to write default: break; doesn't fix that.

Whenever I do write then, I do it reluctantly and mostly as a last resort.

So I do it when it makes sense that I only want to do something for the cases I specify.

But that's not my point. If your code is:

switch(x)
{
  case 0:
    doCase0;
    break;
  case 1:
    doCase1;
    break;
}

And you forgot case 42, it doesn't say "you forgot case 42". It says "you forgot the default case". So then you add default: break; and case 42 is still not there -- because you forgot it.

If you refactor, and default: break; is already there, it doesn't complain.

It reminds me of an old Internet meme (maybe? This might have been before memes) which goes something like:

delete myfiles

are you sure?
yes

This will remove 'myfiles', are you really sure about this? you can't get it back.

YES GODDAMMIT, just delete it already!!!

'myfiles' has been deleted

Ah crap...
>

Having the default be open-endedness means I don't get reminded about that ugly detail anymore.

Of the two evils, I prefer the explicit one.

The open-endededness is not that you forgot a case, it's that you didn't care about that case, or it's not something that requires a case in that particular switch.

I really am puzzled by the idea that the compiler requiring one to put default: is helping them remember things.

To be honest, I'm OK with the requirement. I really am, I can write default: break; and I actually do with regularity without forgetting! But it's a hard one to explain to newcomers. It's just one of those things that I explain with "D makes you do this, get over it". I can't really explain that it's helpful with a straight face.

-Steve

April 01, 2023
On 4/1/23 01:52, Steven Schveighoffer wrote:
> 
> The open-endededness is not that you forgot a case, it's that you didn't care about that case, or it's not something that requires a case in that particular switch.
> ...

But which one is it? I'd like to know when I read the code.

> I really am puzzled by the idea that the compiler requiring one to put `default:` is helping them remember things.
> ...

The point of `default: break;` is that it explicitly states "I am doing nothing if it's not one of the other cases". A switch can be pretty long, so it's convenient to be able to search for the default case and be confident you won't end up in an unrelated long switch.

> To be honest, I'm OK with the requirement. I really am, I can write `default: break;` and I actually do with regularity without forgetting! But it's a hard one to explain to newcomers. It's just one of those things that I explain with "D makes you do this, get over it". I can't really explain that it's helpful with a straight face.

There are multiple common ways to deal with the default:

- You actually meant to use `final switch`.
- Do some explicit action
- assert(0);
- enforce(0,"TODO: handle case")
- Do nothing

I seldomly want to do nothing. Why should that be the implicit default?
April 01, 2023

On Friday, 31 March 2023 at 15:20:41 UTC, Steven Schveighoffer wrote:

>

Why not? How much does this rule help you, vs. annoy you?

If it wasn't for the final keyword (doesn't exist in many languages!), we could discuss this topic. Am I wrong, using final removes default.

If there is a possibility that the switch value may not match any of the specified cases, you should include a default case. In this case, you would not use final switch, as the optimization relies on the assumption that the switch value will always match one of the specified cases.

IMO, we should discuss "should the break keyword be removed"

SDB@79

April 01, 2023

On Saturday, 1 April 2023 at 03:25:15 UTC, Salih Dincer wrote:

>

IMO, we should discuss "should the break keyword be removed"

I believe the reason D requires you to write break rather than making it automatic/implicit is to avoid unexpected behavioral changes when porting C code to D. For example, if you have C code like this:

switch (x) {
    case 1:
        do_stuff();
        /* fallthrough */
    case 2:
        do_other_stuff();
        break;
}

...and you try to compile it as D code, you will get a compile error, because D requires either break, return, or goto case at the end of each case block.

If the D compiler inserted an implicit break; at the end of the first case block automatically, then instead of an error, you would get different behavior at runtime. This would make porting C projects to D more error prone.

April 01, 2023

On Saturday, 1 April 2023 at 04:51:18 UTC, Paul Backus wrote:

>

If the D compiler inserted an implicit break; at the end of the first case block automatically, then instead of an error, you would get different behavior at runtime. This would make porting C projects to D more error prone.

Paul touched on a very good sub-topic. Just as the goto case protects us (this was added recently)), the default and the final duo allow us to choose. Therefore, if there is a final, there should be no break and goto case.

I think 2 modes are enough:

  • final and switch
  • switch and default

SDB@79