Live data from Hacker News

Things Rust shipped without

graydon2.dreamwidth.org

61–70 of 330 posts

Re: Things Rust shipped without

#61
post #54

Earlier quoted context omitted.

rustfmt (currently in development) follows this principle.

Please don't make it an option. Use one format to rule them all, as with "gofmt". I don't care what it is, but pick something and standardize.

It seems you are misunderstanding the situation because you are talking about not making “it” an option and you are then reiterating what I also said.

Even if I repeat myself: I think there should be one default formatting that is standardized and there should be the option to emit in other formats such that everyone can read in the individually preferred format.

With fmt you don't need to establish formatting rules on a project basis, anymore. Everybody can just configure their editor to format the code how they want it to look. That is why I think rustfmt should be compilable as a library, too.

Re: Things Rust shipped without

#62

Earlier quoted context omitted.

The idea that "[u]sing a return as the last line of a function works, but is considered poor style" irks me so much. A lot of what I find appealing about Rust is that it makes so much explicit through its type system, so I don't understand the philosophy behind preferring implicit returns, especially since you could have a scenario where someone hasn't finished writing a function but it still compiles without error.

Implicit returns encourage functional style; foo.map(|x| x + 1) is so much nicer than foo.map(|x| { return x + 1; }). Once you have implicit returns in closures, you might as well have them everywhere for consistency.

Why "might as well"? Things can be optimal in some places and suboptimal elsewhere.

Re: Things Rust shipped without

#63
post #50
post #33

Earlier quoted context omitted.

I've never had to use 'goto' in C++ except to break from a nested loop. In C++ labeled breaks would make 'goto' completely obsolete. In C it would still have use in the implementation of orderly error handling -- the pattern where you hand-implement exception handling in C by putting an on_error: label at the end of the function that is goto'd on error. The addition of some orderly construct for this in C would elimi…

>In C++ labeled breaks would make 'goto' completely obsolete. In 20 years of using C/C++, I've found one use for "goto" that's hard to substitute: simulating coroutines that yield to an outer context. (Similar to C# yield return). A "break label" gets you out of a loop. I needed to use "goto" to jump back into the middle of a loop to resume where the "coroutine" previously left off. The keywords "break/setjmp/longjmp…

I've used a switch statement essentially as a jump table for this purpose. I'm curious whether that would have worked, or if you were doing something different enough that you needed a goto.

Re: Things Rust shipped without

#64
post #59
post #58

What is wrong with UTF-16 support?

It's an encoding that isn't good at anything: it's neither ASCII-compatible (like UTF-8), nor fixed-length (like UTF-32), but because most characters require only 2 bytes, developers frequently assume that none require more, leading to bugs when a character eventually is represented by 4 bytes.

I don't know much about Rust and Rust library, so I have a question: what if I what to develop Windows only software in Rust, will I need to convert back and forth between UTF-16 and UTF-8 (or whatever Rust uses in other parts of the library)?

Re: Things Rust shipped without

#65
post #11
post #9

Earlier quoted context omitted.

No undefined behavior. Perfectly legal since C99 standard.

Can we please get a quote on this one. If reinterpreting memory via a union is valid in C99, including data vs function pointers, then so would reinterpreting that memory via a cast, which would seem to violate one of the most elementary aspects of the standard (e.g. such a rule would be difficult or impossible to implement on a Harvard architecture machine, which the standard previously made plenty of allowance for)

The C standard does not allow casting directly between function pointers and other pointers; they might not even be the same size. Using a union or memcpy will allow you to standards-compatibly reinterpret the bit pattern of a function pointer as a data pointer or vice versa (modulo size differences), but creating the resulting pointer, even without dereferencing it, might cause a crash if the bit pattern is a "trap representation", and in any case dereferencing it isn't guaranteed to do anything useful. ...Not that a portable program has any business trying to read instruction opcodes in the first place.

Re: Things Rust shipped without

#66
post #12

What about things that Rust shipped without that should have been included?

Tail call optimization

https://mail.mozilla.org/pipermail/rust-dev/2013-April/00355...

https://github.com/rust-lang/rust/issues/217

> I'm sorry to be saying all this, and it is with a heavy heart, but we tried and did not find a way to make the tradeoffs associated with them sum up to an argument for inclusion in rust.

> -Graydon

Re: Things Rust shipped without

#67
post #55

Earlier quoted context omitted.

Implicit returns encourage functional style; foo.map(|x| x + 1) is so much nicer than foo.map(|x| { return x + 1; }). Once you have implicit returns in closures, you might as well have them everywhere for consistency.

I think that the "expression-style" return looks good for short and "expression-like" function. Good fn inc(a: u32) -> u32 { a + 1 } fn foo(a: u32, b: u32) -> u32 { let x = a + b; a * x } Bad fn bar(...) -> bool { let mut success = false; let conn = getConnection(); ... if x > y { return false; } else if z It looks especially bad when the function has multiple early returns, and then the final return looks different.

Sheesh, it's such a little thing. "success" vs "return success;"

Re: Things Rust shipped without

#68
post #33
post #26

Earlier quoted context omitted.

I wish C/C++ had a labeled break construct, like JavaScript, Java, Rust, and other languages have. It's surprisingly powerful, while still remaining structured. I personally have enjoyed learning about it, and about just how rarely an actual goto is really needed.

I've never had to use 'goto' in C++ except to break from a nested loop. In C++ labeled breaks would make 'goto' completely obsolete. In C it would still have use in the implementation of orderly error handling -- the pattern where you hand-implement exception handling in C by putting an on_error: label at the end of the function that is goto'd on error. The addition of some orderly construct for this in C would elimi…

A bitecode interpreter is another place where it's nice to have gotos.

Here's the base code without gotos:

  typedef enum { ADD, MUL, ..., END } opcode;

  void run() {
    opcode ins;

    while (1) {
      ins = fetch_next_inst();
      switch (ins) {
        case ADD:
          perform_addition();
          break;
        case MUL:
          perform_multiplication();
          break;
        ...
        case END:
          wrap_up();
          return;
      }
    }
  }
You have 3 jumps on each loop. From the break to the end of the loop, then from the end to the top, and one from the switch to the right case. The first one might be optimized away, but let's remove it explicitly.

  typedef enum { ADD, MUL, ..., END } opcode;

  void run() {
    opcode ins;

    start:
    ins = fetch_next_inst();
    switch (ins) {
      case ADD:
        perform_addition();
        goto start;
      case MUL:
        perform_multiplication();
        goto start;
      ...
      case END:
        wrap_up();
        return;
    }
  }
Assuming a non lousy compiler, we haven't improved anything yet. But now the fun starts. We can go down to one jump for each iteration.

  typedef enum { ADD, MUL, ..., END } opcode;

  #define NEXT() \
  do { \
    ins = fetch_next_inst(); \
    goto *jump_table[ins]; \
  } while(0)

  void run() {
    opcode ins;
    static void *jump_table[] = { &&add_l, &&mul_l, ..., &&end_l };

    NEXT();
    add_l:
      perform_addition();
      NEXT();
    mul_l:
      perform_multiplication();
      NEXT();
    ...
    end_l:
      wrap_up();
      return;
  }
Voila! a single jump every time around. Now, depending on what kind of architecture you're running on, the size of the cache, etc, this may or may not be faster.

Granted, this is not the kind of code you write everyday. But sometimes speed matters, and good luck writing this without gotos.

Re: Things Rust shipped without

#69
post #8
post #6

> goto (not even as a reserved word) I haven't done this for a while, but once upon a graduate program I wrote a compiler from a made-up-language (MUP) to C. MUP had some strange control structures, and if C did not have "goto", it would have been a lot more difficult to implement those structures. Since then, I have always thought languages should have a "goto" statement that human-written code is not allowed to use…

It often simplifies a lot of the compiler implementation if you only have reducible control flow. LLVM is fine with irreducible control flow, because it has to handle C, but rustc uses a CFG for the borrow checker, which is flow-sensitive. You can convert irreducible control flow to reducible control flow, but it can explode the size of the graph in pathological cases. (I don't recall whether the borrow checker actua…

At least reserve the word, in case you change your mind in the future? I guess it's too late now...

Re: Things Rust shipped without

#70
post #68
post #33

Earlier quoted context omitted.

I've never had to use 'goto' in C++ except to break from a nested loop. In C++ labeled breaks would make 'goto' completely obsolete. In C it would still have use in the implementation of orderly error handling -- the pattern where you hand-implement exception handling in C by putting an on_error: label at the end of the function that is goto'd on error. The addition of some orderly construct for this in C would elimi…

A bitecode interpreter is another place where it's nice to have gotos. Here's the base code without gotos: typedef enum { ADD, MUL, ..., END } opcode; void run() { opcode ins; while (1) { ins = fetch_next_inst(); switch (ins) { case ADD: perform_addition(); break; case MUL: perform_multiplication(); break; ... case END: wrap_up(); return; } } } You have 3 jumps on each loop. From the break to the end of the loop, the…

That's pretty nifty, but it seems like something a really good compiler could achieve automatically. Of course I'm not sure if any compilers actually are that good.
Post reply on HN