|
|
Subscribe / Log in / New account

MISRA

MISRA

Posted Sep 18, 2021 20:29 UTC (Sat) by tialaramex (subscriber, #21167)
Parent article: Conill: The long-term consequences of maintainers’ actions

However I also want to react to “They don’t run on C because C is a good language with all the latest features, they run on C because the risks and mitigations for issues in C programs are well-understood and documented as part of MISRA C.”

MISRA is to a considerable extent making up for inadequacies or outright failings in C. Not so long ago somebody wrote on Hacker News that their safety-critical firmware is in Rust and for certification they were asked to provide some similar document to MISRA so, they just took the MISRA C doc and crossed out all the parts which were irrelevant for Rust, which, of course, is most of the MISRA C document. They're not kidding.

Let's take MISRA's rules 16.x - MISRA jealously guards copyright over their document and so I shan't recite any of it, but all of these 16.x rules are about C's switch statement. Now, a very naive programmer might say "Rust doesn't have switch so that's irrelevant" and of course that won't do at all, MISRA isn't worried about the specific word *switch*. But, when we look at Rust's match we see that it covers off almost all of MISRA's concerns, not as optional warnings, but as facets of the core language design.

Some of the 16.x rules are forbidding things no sane C programmer would do, and many would be surprised to discover are legal, such as trying to declare variables in one part of a switch and then use them in a different part even though presumably either declaration or usage won't happen when the statement is executed. Sure enough these things aren't legal Rust. But what's much nicer is that Rust also covers off some MISRA rules that apply to real C code that real programmers write. MISRA is worried about two things C programmers actually do that can hide terrible faults. Falling through, and inexhaustive matching. The C rules cover these by requiring break; religiously for each clause, and by requiring default religiously in every switch statement.

For Rust's match there is no fallthrough, so MISRA needn't forbid it, and all matches are exhaustive. If your match isn't exhaustive it won't compile. In fact Rust goes further, for API stability Rust provides #[non_exhaustive] enumerations which says, as the programmer of this data structure I don't promise I listed all the options yet, and so if you match on this, you must have a default case. For any other structures, you can't add a default case (it's redundant and causes a compiler error) if you in fact exhaustively handled every case. So, you get the benefit of MISRA's exhaustiveness *and* the benefit of common C compiler warnings for unnecessary default that often must be disabled for MISRA code.

One last 16.x rules stands out. MISRA really wants you to write the default case last, or first, but certainly not in the middle where you might lose it. It seems as first as though Rust has no similar rule, and we might need a lint here. But no! If you try to write your default case before other cases in Rust they are made redundant, and the compiler warns you this is a bad idea. You can ignore that warning, but MISRA elsewhere tells you to act on all the warnings if possible.

Now of course many MISRA directives (in particular) can't be trivially enforced by tools, human judgement is required. But even here Rust is often far ahead of C. For example MISRA wants you to document and test things, Rust's infrastructure automatically infers Markdown format documentation, one extra / in the comment above your function and it goes from merely commentary in the source code to HTML documentation that's automatically built for publication. If you write *example code* in that comment just in the ordinary way you would with Markdown, the test infrastructure automatically tries to run the code to check it actually works at the same time it runs any actual unit tests your wrote.


(Log in to post comments)

MISRA

Posted Sep 19, 2021 5:14 UTC (Sun) by NYKevin (subscriber, #129325) [Link]

> Some of the 16.x rules are forbidding things no sane C programmer would do, and many would be surprised to discover are legal, such as trying to declare variables in one part of a switch and then use them in a different part even though presumably either declaration or usage won't happen when the statement is executed. Sure enough these things aren't legal Rust. But what's much nicer is that Rust also covers off some MISRA rules that apply to real C code that real programmers write. MISRA is worried about two things C programmers actually do that can hide terrible faults. Falling through, and inexhaustive matching. The C rules cover these by requiring break; religiously for each clause, and by requiring default religiously in every switch statement.

Just riffing off of this point: A switch statement is a computed goto in a funny hat. It happens to be the least-bad way of expressing "At compile time, I have N different possibilities, and I want to select and execute exactly one of them at runtime," but it isn't actually designed to provide that invariant. You can freely interleave case statements and any other program logic you like, leading to abuses like Duff's device (admittedly, an extreme example, which I have no doubt that MISRA forbids six ways to Sunday).

OTOH, a Rust match statement, to my understanding, *is* specifically designed to provide the "exactly one branch gets executed" invariant, and so you don't need (as many) rules telling you how to use it.

MISRA

Posted Sep 19, 2021 18:12 UTC (Sun) by tialaramex (subscriber, #21167) [Link]

Technically match is actually an expression, and so it's logically _obliged_ to follow only one arm because the expression obviously only has one value, and in fact the values in each arm must be type compatible, the Rust compiler will conclude that whichever type is compatible with all the arms is in fact the type of the expression, if there is no suitable type that's an error.

https://play.rust-lang.org/?version=stable&mode=debug...

The compiler points out that two of the arms are clearly integers while another is an &str (in this case a string literal).

In many cases the idiomatic Rust way to express what you wanted actually does involve values from each arm, but of course it is possible for your match to be full of procedural code in which case those values are just the empty tuple -- or for it to have statements which outright leave the match (such as "return" or "break") and these are in fact type compatible with anything.

MISRA

Posted Sep 21, 2021 6:26 UTC (Tue) by flussence (subscriber, #85566) [Link]

> But even here Rust is often far ahead of C. For example MISRA wants you to document and test things, Rust's infrastructure automatically infers Markdown format documentation, one extra / in the comment above your function and it goes from merely commentary in the source code to HTML documentation that's automatically built for publication. If you write *example code* in that comment just in the ordinary way you would with Markdown, the test infrastructure automatically tries to run the code to check it actually works at the same time it runs any actual unit tests your wrote.

Slightly off topic, but after having to live with Raku's take on code documentation I'm kind of jealous of the batteries-included simplicity of Rust here.


Copyright © 2023, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds