top | item 10444378

(no title)

copsarebastards | 10 years ago

> This is a case of following the letter of the law (in this case the C standard) while disregarding its spirit: all the undefined behaviour was so that C compilers could accomodate for odd architectures while remaining close to the metal, not so that compiler programmers could go out of their way to turn their compiler into a mine field.

Computers don't have spirits; they work as you tell them to work, to the letter, and if you're remaining close to the metal, your language will indicate that fact. Optimizing undefined behaviors doesn't make C a minefield; low-level programming for different architectures just is inherently a minefield. C was a minefield before these optimizations were added.

Rust is extremely impressive because they've found so many ways to do high-level programming while maintaining low-level performance. But they can only do that because they have the benefit of the 4 decades of programming language research that have occurred since the basics of C were designed.

discuss

order

EdiX|10 years ago

>Computers don't have spirits

But standards committee do.

>Rust is extremely impressive because they've found so many ways to do high-level programming while maintaining low-level performance. But they can only do that because they have the benefit of the 4 decades of programming language research that have occurred since the basics of C were designed.

I doubt rust could be ported to a 8bit PIC microcontroller, or to a 6502 keeping reasonable performance characteristics or letting the programmer take advantage of the platform quirks. It's not just "4 decades of programming language research" it's also that it's intended to work only on "modern" processors.

copsarebastards|10 years ago

>> Computers don't have spirits

> But standards committee do.

Agreed. Which is why you should choose a language which was standardized by a standards committee whose goals better align with your goals.

> I doubt rust could be ported to a 8bit PIC microcontroller, or to a 6502 keeping reasonable performance characteristics or letting the programmer take advantage of the platform quirks.

I don't think that's true; I think that the current state of Rust tools is such that this is true now, but it's nothing inherent to the design of the language, and I think you'll be able to do quite a bit with Rust in the situations you describe when the tools around Rust are more mature. I can't really speak to this more because I'm not sure why you think this can't be done.

geofft|10 years ago

> I doubt rust could be ported to a 8bit PIC microcontroller, or to a 6502 keeping reasonable performance characteristics

I don't believe this is correct. Most of why Rust avoids UB is that it uses static types much more effectively than C does. Static types are an abstraction between the programmer and the compiler for conveying intent, that cease to exist at runtime. So the runtime processor architecture should be irrelevant.

For instance, in C, dereferencing a null pointer is UB. This allows a compiler to optimize out checks for null pointers if it "knows" that the pointer can't be null, and it "knows" that a pointer can't be null if the programmer previously dereferenced it. This is, itself, a form of communication between the programmer and the compiler, but an imperfect one. In Rust, safe pointers (references) cannot be null. A nullable pointer is represented with the Option<T> type, which has two variants, Some(T) and None. In order to extract an &Something from an Option<&Something>, a programmer has to explicitly check for these two cases. Once you have a &Something, both you and the compiler know it can't be null.

But at the output-code level, a documented compiler optimization allows Option<&Something> to be stored as just a single pointer -- since &Something cannot be null, a null-valued pointer must represent None, not Some(NULL). So the resulting code from the Rust compiler looks exactly like the resulting code from the C compiler, both in terms of memory usage and in terms of which null checks are present and which can be skipped. But the communication is much clearer, preventing miscommunications like the Linux kernel's

    int flags = parameter->flags;
    if (parameter == NULL)
        return -EINVAL;
Here the compiler thinks that the first line is the programmer saying "Hey, parameter cannot be null". But the programmer did not actually intend that. In Rust, the type system requires that the programmer write the null check before using the value, so that miscommunication is not possible.

There are similar stories for bounds checks and for loops, branches and indirect jumps and the match statement, etc. And none of this differs whether you're writing for a Core i7 or for a VAX.

> or letting the programmer take advantage of the platform quirks.

I'm not deeply familiar with that level of embedded systems, but at least on desktop-class processors, compilers are generally better than humans at writing stupidly-optimized code that's aware of particular opcode sequences that work better, etc.

(There are a few reasons why porting Rust to an older processor would be somewhat less than trivial, but they mostly involve assumptions made in the language definition about things like size_t and uintptr_t being the same, etc. You could write a language with a strong type system but C's portability assumptions, perhaps even a fork of Rust, if there were a use case / demand for it.)

lmm|10 years ago

You don't need 4 decades of programming language research to specify that e.g. signed integer overflow either returns an implementation-defined value or the program aborting. There are languages older than C that allowed the useful forms of bit-twiddling but offered much stronger safety guarantees.

ridiculous_fish|10 years ago

But you pay a performance cost for either of those decisions. Consider code like this:

    for (int i=0; i <= N; i++) func();
Most processors have special support for looping a fixed number of times, e.g. "decrement then branch if zero." If overflow is UB, the compiler can use this support.

But if overflow returns an implementation defined value, then it is possible that N is INT_MAX and the loop will not terminate. In this case the compiler cannot use the fixed-iteration form, and must emit a more expensive instruction sequence.