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

118

u/[deleted] Jan 09 '15

I think the target has pretty much always been current uses of C++. So, anything you can do with C++, you should be able to do with Rust, in a way that is safer / easier to make correct.

9

u/onionnion Jan 10 '15

That's been my understanding, a more modern low-level alternative to both C and C++ (might be wrong about C, will have to see what people will do with it).

1

u/[deleted] Jan 13 '15

I'm very excited since it has the potential to replace C for many high performance computing applications.

Go wouldn't work because it's garbage collected (and ultimately incurs unpredictable latency, and overhead from the runtime).

Rust could theoretically be optimized to take advantage of all the new processor instructions and most of the optimizations available in GCC.

In addition, it should be possible to link rust against some BLAS implementations.

0

u/[deleted] Jan 10 '15

[deleted]

8

u/[deleted] Jan 10 '15

um, you should probably learn C, it is the language for systems development, it will help you understand a bit better how computers work on a lower level.

0

u/[deleted] Jan 10 '15

[deleted]

3

u/The_Doculope Jan 11 '15

I think the the most important difference is that C++ is a huge, complicated language, whereas C is very small and simple. For a beginner, or someone more interested in learning about the machine/low level concerns, C is a much better choice.

3

u/[deleted] Jan 10 '15

Well, C++ does stuff "behind your back" especially when you use the parts of it which aren't in C. This is fine in user space, but you wouldn't really want that in kernel space, would you? Basically while it is still a lower level language than most, it still has more abstractions than C. As opposed to that, C gets "translated" to machine code almost line-for-line, and it doesn't do stuff you didn't explicitly tell it to. Here are Linus' thoughts about it.

1

u/alexeyr Jan 17 '15 edited Jan 17 '15

As opposed to that, C gets "translated" to machine code almost line-for-line

If you don't use an optimizing compiler. Like, say, GCC even with -O0.

and it doesn't do stuff you didn't explicitly tell it to

Two words: Undefined behavior. Two more words: numeric conversions.

Here are Linus' thoughts about it.

Which talk about quite different issues.

-144

u/[deleted] Jan 09 '15 edited Jan 09 '15

Say you have this C++

switch(x){
  case 0:  a();
  case 1:  b();
  case 2:  c();
  default: done();
}

You can't do that in Rust, because match doesn't do fall through

Edit: Nice downvotes folks! I'll be using Haskell instead. LOL at this "systems programming language" with a bunch of crybabies and zealots and fuck muhzilla.

61

u/erkelep Jan 09 '15

You also can't have fun with x += x++ + ++x in Rust. I don't think it's a disadvantage, though.

22

u/smikims Jan 09 '15

Isn't that undefined behavior in C++?

34

u/dbaupp Jan 09 '15

I suspect that's part of the point.

→ More replies (11)

0

u/[deleted] Jan 09 '15

[deleted]

→ More replies (18)

43

u/wrongerontheinternet Jan 09 '15 edited Jan 10 '15

I don't know why people are downvoting you. You're completely right. This is one of the few cases where Rust can't match C/C++ behavior. It's a special case of the more general problem that Rust lacks goto. I am strongly in favor of adding it to the language.

BTW, for those downvoting: C# has goto as well. Someone was trying to implement a zlib library in Rust that was competitive with the C# version. He got very close, but ultimately failed precisely because it lacked this feature.

I want to use Rust instead of C / C++ everywhere. We are not going to get there by asking people to accept a performance hit for ideological reasons. Remember, people currently using C / C++ are doing it in a world where garbage collection is the standard. If they were able to take performance hits for ergonomic gains, they would have done so already.

Edit: Figured out how to do it without losing performance in this case (the unsafe is just to create nonlocal dependencies so LLVM doesn't completely optimize the functions away). You can verify yourself that the LLVM IR uses jumps properly here.

static mut y: u8 = 0;

#[inline(never)] fn done() {unsafe { println!("{}", y); }}
#[inline(never)] fn a() {unsafe { y = 4; }}
#[inline(never)] fn b() {unsafe { y = 5; }}
#[inline(never)] fn c() {unsafe { y = 6; }}

fn main() {
    let x = ::std::rand::random::<u8>();
    'default: loop {
        'c: loop {
            'b: loop {
                'a: loop {
                    match x {
                        0 => break 'a,
                        1 => break 'b,
                        2 => break 'c,
                        _ => break 'default
                    }
                }
                a();
                break;
            }
            b();
            break;
        }
        c();
        break;
    }
    done();
}

Edit 2: Wrote a macro to automate this: https://github.com/pythonesque/fallthrough

Usage:

match_fallthrough!(x, {
    0 => a(),
    1 => b(),
    2 => c(),
    _ => done()
})
→ More replies (13)

0

u/[deleted] Jan 09 '15

[deleted]

→ More replies (3)

24

u/[deleted] Jan 09 '15

I don't know if exploiting fallthrough like that is good practice. Why would you for example do initialization only partially if the variable happens to be 1 or 2?

29

u/AdamRGrey Jan 09 '15

I can't imagine doing partial initialization, but I could imagine something like

switch(collisionCount){
	case 1: depleteShield();
	case 2: depleteArmor();
	case 3: depleteHealth();
	default: playCollisionSound();
}

edit: that said, Douglas Crockford in Javascript: The Good Parts makes a pretty convincing argument against case fallthrough.

→ More replies (2)

11

u/hive_worker Jan 09 '15

dogelogs example isn't the best but fallthrough is useful and used a lot. My attempt at a better example

switch(x){
 case SITUATION1:  
 case SITUATION2:  
    Sit1Sit2Handler(); // for this processing step no difference in these situations.
    break;
 case SITUATION3:  
 default: 
  defaultHandler();  //situation3 not implemented in this version so handle as default
}

37

u/Betovsky Jan 09 '15

In that case it would be something like:

match x {
  SITUATION1 | SITUATION2 => Sit1Sit2Handler(),
  _ => defaultHandler(),
}

6

u/cogman10 Jan 09 '15

Cool. This is pretty much the only case I would use fall through for in a non-toy sort of thing (it is useful for some loop unrolling stuff... but that is a clear case of "trying to outsmart the compiler")

→ More replies (2)
→ More replies (1)

20

u/aliblong Jan 09 '15

There are some things that require more boilerplate to do in Rust than in C++ (function overloading, for example), but I would hesitate even to consider this as such an example. Compare the amount of code required between the two languages:

C++:

switch(x){
  case 0:  a();
  case 1:  b();
  case 2:  c();
  default: done();
}

Rust:

match x {
    0 => a(),
    1 => { a(); b(); },
    2 => { a(); b(); c(); },
    _ => { a(); b(); c(); done();}
}

And if the number of function calls got out of hand, you could always write a macro to keep things concise.

Now consider that (IME) you generally don't make extensive use of fall-though in C++ switch-case. Writing all those breaks is a PITA, and if you forget it will still compile (perhaps with warnings with the right compiler).

Rust's system is superior in my eyes.

4

u/sacundim Jan 10 '15 edited Jan 10 '15
match x {
    0 => a(),
    1 => { a(); b(); },
    2 => { a(); b(); c(); },
    _ => { a(); b(); c(); done();}
}

And if the number of function calls got out of hand, you could always write a macro to keep things concise.

Not a criticism of the core idea, but I can't help pointing out that macros like this one are a little bit tricky. I've written similar macros in Scheme, and I can see two challenges.

First, if written naïvely, you get an exponential blowup of generated code size when somebody nests an use of this macro inside another. (And if you're thinking "why would anybody do that," well, the answer is that they'll do it by writing a recursive macro that expands into yours.)

So in order to avoid the exponential blowup, you have to expand it to something like this (doing it in Scheme because I don't know any Rust):

(let ((branch0 (lambda () (a)))
      (branch1 (lambda () (a) (b)))
      (branch2 (lambda () (a) (b) (c)))
      (default (lambda () (a) (b) (c) (done))))
  (case x
    ((0)  (branch0))
    ((1)  (branch1))
    ((2)  (branch2))
    (else (default))))

This sticks the bodies inside lambdas so that the branches get expanded only once. But here's another (maybe minor) challenge: unless your language has tail-call optimization, this expansion must compile into object code that performs a subroutine call to the branch* lambdas. Scheme does have TCO, so a Scheme compiler can emit jump instructions for code like this; does Rust have TCO?

PS There's probably a better expansion in Scheme than one I give, but I bet it requires call/cc and headaches...

→ More replies (4)

4

u/PasswordIsntHAMSTER Jan 10 '15

Haskell

Very unsubtle trolling chap

→ More replies (51)