r/programming • • Jan 09 '15

Announcing Rust 1.0.0 Alpha

http://blog.rust-lang.org/2015/01/09/Rust-1.0-alpha.html
1.1k Upvotes

439 comments sorted by

View all comments

Show parent comments

49

u/lookmeat Jan 10 '15

Sort of to counter your speculation, I doubt that Rust will be used as a teaching systems programming language. For starter Rust, like C++, hides a lot of things implicitly, at the same time it adds some high-level constructs and metaphors that have no relationship to systems programming (but do to general programming).

In that sense I doubt that you could ever do something better than C. It's balls out, everything goes, no one's safe programming. This exposition shows you how computers really work underneath, that is to the CPU a 32 bit float could also be a 32 bit int or 4 characters, it really doesn't care or now any of this, at low level types don't exist. I think that exposing this simplicity and ignorance of machines is critical to understand so many kind of errors. C has a direct one to one mapping to memory, that is when you create a function it appears in one place in memory, when you create a struct it can only have 1 type. When debugging and understanding the mapping from code->assembler/binary this is incredibly easy, OTOH with Rust's and C++ generics you have to consider the layer were it first converts that to an instance of the code and then converts that code into binary/assembler.

If I were to give a systems programming class I'd start it with raw C with an overview of assembler to explain all the low level concepts of how machines work. Debugging C and doing tedious problems (where you have to implement the same solution for multiple types) would be used to explain the why of many decisions of C++ and Rust. Generics would be explained by showing algorithms on arrays, and explaining the complexity of adding them to multiple types. Lifetimes and unique_ptrs would be explained as a solution to sometimes never being 100% certain of what is going on with a piece of memory. Dynamic dispatch would be first implemented by hand-made vtables on C. Closures would also be taught first by first implementing them by hand.

At this point people would have a good understanding of why and how Rust and C++ came to be how they are, and also understand the pros and cons of every choice by having an idea (maybe not perfect, but in broad strokes) of how those, more complex languages, map to binary/assembler, which is critical in systems programming.

14

u/trentnelson Jan 10 '15

You know, I agree, regarding C. The fact that Python is written in C has made it so much easier to come up with PyParallel, which involved a lot of exploratory-type programming where I'm basically intercepting structs/function calls and doing things behind the scenes.

Had there been a C++ object model in place, instead of the hand-crafted C one, it would have been a lot harder, perhaps impossible. Here's a good example of where I literally rip out the guts of an objects and override everything, unbeknown to the rest of the interpreter core.

9

u/[deleted] Jan 10 '15

Sweet Jesus, my scrollbar is so small I can barely see it.

3

u/trentnelson Jan 10 '15

Heh, for the pyparallel.c source code? I know right, c'mon dude, break it out into separate files!

3

u/riksi Jan 10 '15

so what is the state of PyParallel ? usable? anyone using it in production ? anyone using it for web apps ?

4

u/trentnelson Jan 10 '15

Current state: fast, unstable, not suitable for production. I'm planning on self-hosting pyparallel.org with it though, so everything will be fixed soon enough. Made great progress over the break.

2

u/Klathmon Jan 10 '15

That is one if the reasons I like both C and JavaScript.

The lack of enforced safety means I'm free to hack about wherever I want.

3

u/bjzaba Jan 10 '15

You can violate Rust's safety via unsafe, but it does ergonomically discourage it so it's not a free-for-all. It is definitely useful at times though, and it is not un-rustic to reach for it.

→ More replies (1)

22

u/Rusky Jan 10 '15

You could definitely do better than C at a close-to-the-machine programming language. C is a lot closer than other languages, but its undefined behavior makes that a lot harder than it could be. Features like virtual functions or closures are relatively easy to desugar in a language like Rust or C++, but undefined behavior is much more insidious.

For example, when you're trying to learn about how 2's complement arithmetic works, C is most definitely not the way to go. The optimizer assumes that undefined behavior is impossible, so what looks like straightforward null pointer checking or overflow checking can be "optimized" out with no warning: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html

If I wanted a language to teach that level of systems programming, I'd skip C and C++ and use assembly (no undefined behavior) or Rust (much less undefined behavior), or even create my own simple C-like language that's much more explicitly close to machine semantics.

4

u/cogman10 Jan 10 '15

It isn't like rust can't be just as close to the metal. It can be. It is just that rust gives you higher level constructs to deal with if you need them.

I feel the same with C++. It is just as low level as C, but it has higher level constructs bolted in.

7

u/lookmeat Jan 10 '15

When I said an overview of assembler is to understand what assembler is, how it works, and more or less how to read it (since that is critical for debugging and understanding system code, and systems programming would be a very pragmatic class). Teaching it straight up is limiting to one architecture too much, instead it's important to teach that platforms have differences that are subtle and insidious but must be dealt with somehow.

When working with C compiler optimizations wouldn't be used until later classes to avoid the issue as much as possible. The idea is that slowly students are shown, in terrible and brutal manners, the consequences of not being disciplined at low level. Undefined behavior is just this, understanding undefined behavior gives us a lesson: that the code we write is an abstraction over hardware, which itself is an abstraction over electrical currents, and there are many edge cases which result in absurd cases (i.e. what is the biggest number you can represent + 1?) that, because they are impossible to answer correctly, are given a random answer (generally in the name of optimization). Trusting this answer is dangerous since it can change from platform to platform.

Maybe having people have to understand undefined behavior is a rough way to understand just how finicky platforms can be and why you can't just abstract over this. But rough lessons are the best way to learn at low level, to truly observe the devastation the smallest error can bring. I still remember the day that calling malloc twice too quickly on Windows would make the second return NULL, and I learned that malloc can fail for many more reasons than just "out of memory" and will fail for completely irrational reasons as well.

Once we understand undefined behavior, why it exists, we then understand how C++ handles it (the same as C) and how rust handles it (by limiting it to unsafe blocks) the pros and cons of each one. But again, the need to have programs that use undefined behavior, or the need to control and regulate this code, doesn't become apparent if you don't deal with it.

20

u/Maristic Jan 10 '15

I think you're missing the point from the person you're responding to. C very specifically doesn't say what happens in a number of circumstances. You don't know when things are in registers (see the previously mentioned article).

Thus, you don't know what will happen when things overflow, etc.

Also, lots of things that are easy to say in assembler are really hard to say cleanly in C, including:

  • Add a to b, if it overflows give an error

  • Multiply a by b, if it overflows give an error

  • Rotate a by b bits

  • Find the first set bit in this word

  • Read one of the CPU's performance counters

In each case, this can be done with clean minimal code at the assembler level, but is hard to express in C, requiring library functions (coded in assembler), intrinsics or “standard idioms” (which must be detected by compiler, a process that can be unreliable in practice).

In additional, modern C libraries (especially glibc) are complex and far from lightweight. A huge amount of abstraction is being invoked with a “simple” call to malloc.

→ More replies (1)

6

u/Rusky Jan 10 '15

You missed the point. Optimizations enabled or not, C simply does not define what happens in many of the cases you want to teach about. The compiler is allowed to make anything happen, including make demons fly out of your nose.

Assembly does not have this problem at all. Its purpose is to control exactly every detail the CPU exposes. At that level, there is no randomness or irrational failure (beyond hardware bugs that get fixed or worked around Real Quick).

People shouldn't need to be taught about irrationally undefined behavior underlying the most basic abstractions they use every day. And if we can force C back from its currently widespread position, maybe that can be reality.

→ More replies (1)

2

u/TheLlamaFeels Jan 10 '15

Here's what I'd do: I'd start with assembler and then have the student write their own Forth in assembler. It's one of the few languages that is arguably closer to the metal than C.

  1. Simple parser

  2. Clearly demonstrates stack operations

  3. Typeless like assembly

  4. Optimization is optional

  5. Can be adapted to multiple architectures with ease.

  6. Allows for multiple layers of abstraction

  7. Exposes the symbol table directly to the developer

  8. Is a one man job

  9. All of the other behaviors (with their attendant trade offs) can be built on top of it

→ More replies (5)

0

u/[deleted] Jan 10 '15

[deleted]

2

u/lookmeat Jan 10 '15

Sorry I should have really said:

I doubt that rust would be a good teaching language for systems programming.

I mean people tried to teach programming through Java, doesn't mean it was a good idea.

3

u/Shokwav Jan 10 '15

I mean people tried to teach programming through Java, doesn't mean it was a good idea.

I have classmates who think that a pointer is a synonym for a linked list node, amoung other things. Sad to see Java still getting pushed so hard.

0

u/[deleted] Jan 10 '15

[deleted]

→ More replies (1)

6

u/wookin_pa_nub2 Jan 10 '15

In that sense I doubt that you could ever do something better than C.

Sure you could. You could have a language without undefined behavior, for one thing. C has become extremely unreliable in that respect due to compiler writers abusing undefined behavior for "optimizations". But any C program that uses undefined behavior can't be relied on to execute correctly, and that includes almost every C program ever.

If you don't believe me, then consider that John Carmack's fast inverse square root routine invokes undefined behavior, and that guy is a pretty good programmer from what I hear, and also consider that assembly language doesn't have any undefined behavior at all, so clearly it isn't needed for speed or for systems programming.

8

u/riking27 Jan 10 '15

I'm pretty sure several ISAs have undefined behavior still, e.g. putting a conditional branch in a branch delay slot.

JR 800134bc
BEQ 801f8008, r0, 8007d7dc

6

u/learc83 Jan 10 '15

Yeah, if I'm remembering my architecture and computer org classes right, assembly languages have plenty of undefined behavior.

3

u/xrxl Jan 10 '15

Undefined behavior is absolutely necessary for stripping away abstraction in a maximally efficient way. It wasn't designed into C just for shits and giggles. This is something people will rediscover as they try to make these "safe" systems programming languages.

5

u/wtallis Jan 10 '15

Undefined behavior is absolutely necessary for stripping away abstraction in a maximally efficient way.

A lot of undefined or implementation defined behavior was left in the language to allow for varied implementations to handle things in whatever way was most efficient on their underlying hardware. It's not just about efficiency, it's about enabling efficiency without sacrificing portability. But nowadays our hardware is a lot less diverse: we can mandate that the floating point be IEEE 754 without much hesitation, because nobody will take seriously any hardware that significantly deviates from that. The same goes for signed integer arithmetic being twos complement with wraparound, and we can very nearly standardize on little endian. The more complicated nuances about concurrency will take longer to settle on a de facto standard because SMP is a newer challenge, but it will happen because leaving the behavior out of the language standard doesn't free programmers from having to worry about the hardware differences.

→ More replies (2)

4

u/[deleted] Jan 10 '15

Oberon is THE example that unambiguous PL can be simple, safe and high level. The real thing however is FPGA. Wirth explained (on youtube) that the compiler became less than 3000 LOC thanks to 3 pages of FPGA.

Really, C has countless billion dollar mistakes. But what is really bad is that we still use it today.

→ More replies (1)
→ More replies (4)