Hacker Newsnew | past | comments | ask | show | jobs | submit | platinumrad's commentslogin

Two wrongs don't make a right.

"What is soft power?"


The "good" rules are like "don't write off the end of an array", and the bad ones are like "no early returns" or "variable names must not be longer than 6 characters". 95% of the "good" rules are basically just longer ways of saying "don't invoke undefined behavior".


> variable names must not be longer than 6 characters

My memory might be lapsing here, but I don't think MISRA has such a rule. C89/C90 states that _external_ identifiers only matter up to their first 6 characters [1], while MISRA specifies uniqueness up to the first 31 characters [2].

[1] https://stackoverflow.com/questions/38035628/c-why-did-ansi-...

[2] https://stackoverflow.com/questions/19905944/why-must-the-fi...


Why is "no early returns" not a good rule?

I do early returns in code I write, but ONLY because everybody seems to do it. I prefer stuff to be in predictable places: variables at the top, return at the end. Simpler? Delphi/Pascal style.


Early returns makes the code more linear, reduces conditional/indent depth, and in some cases makes the code faster. In short, it often makes code simpler. The “no early returns” is a soft version of “no gotos”. There are cases where it is not possible to produce good code while following those heuristics. A software engineer should strive to produce the best possible code, not rigidly follow heuristics even when they don’t make sense.

There is an element of taste. Don’t create random early returns if it doesn’t improve the code. But there are many, many cases where it makes the code much more readable and maintainable.


The "no early returns" rule came about because was a good rule in context, specifically C and FORTRAN code before roughly 1990. It was part of "structured programming", contemporary to "Go To Statement Considered Harmful", Dijkstra, 1968. And it became received wisdom - i.e. a rule that people follow without close examination.

For example of the rule, a function might allocate, do something and then de-allocate again at the end of the block. A second exit point makes it easy to miss that de-allocation, and so introduce memory leaks that only happen sometimes. The code is harder to reason about and the bugs harder to find.

source:

> A problem with early exit is that cleanup statements might not be executed. ... Cleanup must be done at each return site, which is brittle and can easily result in bugs.

https://en.wikipedia.org/wiki/Structured_programming#Early_r...

About 90% of us will now be thinking "but that issue doesn't apply to me at all in $ModernLang. We have GC, using (x) {} blocks, try-finally, or we have deterministic finalisation, etc."

And they're correct. In most modern languages it's fine. The "no early returns" rule does not apply to Java, TypeScript, C#, Rust, Python, etc. Because these languages specifically made early return habitable.

The meta-rule is that some rules persist past the point when they were useful. Understand what a rule is for and then you can say when it applies at all. Rules without reasons make this harder. Some rules have lasted: we typically don't use goto at all any more, just structured wrappers of it such as if-else and foreach


> And they're correct. In most modern languages it's fine. The "no early returns" rule does not apply to Java, TypeScript, C#, Rust, Python, etc. Because these languages specifically made early return habitable.

Early return is perfectly manageable in C as long as you aren't paranoid about function inlining. You just have a wrapper that does unconditional setup, passes the acquired resources to a worker, and unconditionally cleans up. Then the worker can return whenever it likes, and you don't need any gotos either.


> In C, you just have a wrapper that does unconditional setup, passes the acquired resources to a worker, and unconditionally cleans up. Then the worker can return whenever it like

Right, so you allow early return only in functions that do not have any setup and clean-up - where it's safe. Something like "pure" functions. And you describe a way to extract such functions from others.


I remember having this argument with my professor at the school, who insisted that a function should have only one "return" clause at the very end. Even as I tried, I could not get him to explain why this would be valuable and how does this produce better code, so I'm interested on hearing your take on this?


It helps prevent bugs with state. The apple login bypass bug comes to mind.

Basically, you have code in an "if" statement, and if you return early in that if statement, you might have code that you needed to run, but didnt.

Forcing devs to only "return once" encourages the dev to think through any stateful code that may be left in an intermediate state.

In practice, at my shop, we permit early returns for trivial things at the top of a function, otherwise only one return at the bottom. That seems to be the best of both worlds for this particular rule.


> The apple login bypass bug comes to mind.

I think you're talking about this "goto fail" bug?

https://teamscale.com/blog/en/news/blog/gotofail

> In practice, at my shop, we permit early returns for trivial things

Are you also writing C or similar? If so, then this rule is relevant.

In modern languages, there are language constructs to aid the cleanup on exit, such as using(resource) {} or try {} finally {} It really does depend on if these conveniences are available or not.

For the rest of us, the opposite of "no early return" is to choose early return only sometime - in cases where results in better code, e.g. shorter, less indented and unlikely to cause issues due to failure to cleanup on exit. And avoid it where it might be problematic. In other words, to taste.

> Kent Beck, Martin Fowler, and co-authors have argued in their refactoring books that nested conditionals may be harder to understand than a certain type of flatter structure using multiple exits predicated by guard clauses. Their 2009 book flatly states that "one exit point is really not a useful rule. Clarity is the key principle: If the method is clearer with one exit point, use one exit point; otherwise don’t".

https://en.wikipedia.org/wiki/Structured_programming#Early_e...

this thinking is quite different to say, 25 years earlier than that, and IMHO the programming language constructs available play a big role.


The rule of thumb I use is to only have one return after modifying state that will persist outside the function call.


Thank you!


For me it's mostly about indentation / scope depth. So I prefer to have some early exits with precondition checks at the beginning, these are things I don't have to worry about afterwards and I can start with the rest at indentation level "0". The "real" result is at the end.


> Why is "no early returns" not a good rule?

It might be a good guideline.

Its not a good rule because slavishly following results in harder to follow code written to adhere to it.


Because good code checks pre conditions and returns early if they are not met.


I've (unfortunately) written plenty of "safety critical" code professionally and coding standards definitely have a negative effect overall. The thing keeping planes from falling out of the sky is careful design, which in practice means fail-safes, watchdogs, redundancy, and most-importantly, requirements that aren't overly ambitious.

While maybe 10% of rules are sensible, these sensible rules also tend to be blindingly obvious, or at least table stakes on embedded systems (e.g. don't try to allocate on a system which probably doesn't have a full libc in the first place).


Many coding standards rules have nothing to do with correctness and everything to do with things like readability and reducing cognitive load (“which style should I use here?”)


So you would be okay with your employer putting words in your mouth?


Those are both completely normal things to do when you're implementing a programming language. For example, the Hotspot JVM uses SIGSEGV to stop the world for garbage collection.


C: struct be32_t { uint32_t _ }; struct le32_t { uint32_t _ };

C++: That, but with a billion operator overloads and conversion operators so they feel just like native integers.


In C++ you probably could even make a templated class that implements all possible operators for any type that supports it with concepts. Then you can just `using kilometer = unique_type<uint32_t, "kilometer">` without needing to create a custom type each time.


Though if you do that km times km isn't km it is a volume - so your custom type would be wrong to have all operations. what unit km times km should be isn't clear.


These libraries already exist. God how people underestimate C++ all the time.

Of course you can use a unit type that handles conversions AND mathematical operations. Feet to meter cubed and you get m³, and the library will throw a compile error if you try to assign it to anything it doesn't work with (liters would be fine, for example)


I know of about 7 different libraries, 5 of them private to my company (of which 4 are not in use). Every one takes a fundamentally different approach to the problem.

> Feet to meter cubed and you get m³, and the library will throw a compile error if you try to assign it to anything it doesn't work with (liters would be fine, for example)

Liters would not be fine if you are using standard floating point values since you lose precision moving decimal points in some cases. Maybe for your application the values are such that this doesn't matter, but without understanding your problem in depth you cannot make the generic statement.

I could write books (I won't but I could) on all the compromises and trade offs in building a unit type library.


As a more general rant - people who have maybe used 5% of the feature set of C++ come along and explain why language X is superior because it has feature Y and Z.

News flash, C++ has every conceivable feature, it's the reason why it is so unwieldy. But you can even plug in a fucking GC if you so desire. Let alone stuff like basic meta programming.


GC was removed from the C++ standard in C++23 because all the compilers were like "hell no" and it was an optional feature so they could get away with not adding it. So this optional feature never actually existed and they removed it in later standards.


The C++ standard has never included a garbage collector. It only provided mechanisms intended to facilitate the implementation of a GC, but they were useless.


There are ways to do GC without language support. They are harder, but have been around in various forms for decades. They have never caught on though.


Do they really? Their types really have no custom constructors and you can use designated initializers for your data? I would really much like to have been underestimating C++, could you show an example of such a library?


Thankfully some folks already thought that out, one possible library,

https://mpusz.github.io/mp-units/latest/


I have seen several versions. I wrote two different ones myself - both not in use because the real world of units turned out far more complex. the multiplication thing is one simple example of the issuses but not a complete list


I guess I should have said `unique_type<uint32_t, "meter", std::ratio<1000, 1>>` then :) Then you can do the same as std::chrono::duration does.


Now write a book on the various tradeoffs from that decision. there is no perfect general answer, some domains have specific needs that are different. Depending on your domain that might be a good choice or it might be terrible


who said `operator*` needs to return the same type as its parameters?


operator* can return exactly one type. You can choose which, but metric offers many possible choices, and with floating point math on computers you will lose precision converting between them in some cases so you need to take care to get the right on for your users - which will not be the same for all users.


One return type, for any given combination of parameter types, not to mention the possibility of templating to manipulate the return type….


See, more trade offs...


honestly, I’m not seeing the problem you’re seeing


C++ really needs something like `using explicit kilometer = uint32_t;`

The 'explicit' would force you to use static_cast


We seem to be speedrunning the copying of some of its worst parts. Don't expect to get any of the high-speed rail or abundant housing though.


Where did you get the idea that they don't make eminent domain payments?


Oh, I guess you were talking about Jews then.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: