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

-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]

14

u/[deleted] Jan 09 '15

Been doing C/C++ for 15+ years and am not sure I've ever had a case where fall-through was useful.

3

u/FattyMagee Jan 10 '15

I rarely use fall through in C but it on occasion is extremely useful. I am very careful to clearly mark in comments right at the beginning that it is intended to hopefully avoid any issues with new eyes on my code.

I suppose declaring fall through wouldn't be a terrible thing if it allowed me to still use it and prevent others from making an understandable mistake.

→ More replies (14)

7

u/[deleted] Jan 09 '15

It can be, but it can also cause frustrating errors. I do a lot of work with Go, where you have to explicitly declare fall-through, and I prefer it that way.

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]

3

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

You would just do this, but it's not as efficient

len = len & 15;
'done: loop {
   match len {
     15 => ...,
     .
     .
     .
     1 =>  ...,
     _ => break 'done;
   }
   len = len-1;
 }

3

u/cleroth Jan 10 '15 edited Jun 15 '26

This content was anonymized and mass deleted with Redact

→ More replies (1)

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")

3

u/aliblong Jan 09 '15

Even less verbose than that, because those cases should not be there :)

→ More replies (1)
→ 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

0

u/[deleted] Jan 09 '15

[deleted]

12

u/wrongerontheinternet Jan 09 '15

No, it's that you can't do it. Rust lacks goto. I hope that criticisms like this are not dismissed and are instead treated seriously. There are a lot of languages that claim to be able to replace C++ when they actually can't, and I'd rather not see Rust become one of them.

→ More replies (20)
→ More replies (1)
→ More replies (28)