Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Rust won't make legacy code and legacy coders go away. In fact, as time goes on the amount of crappy legacy Rust code and low-skilled legacy Rust coders will only grow.

So Rust is not a solution.



> In fact, as time goes on the amount of crappy legacy Rust code and low-skilled legacy Rust coders will only grow

But it will make those codebases a lot easier to deal with because the Rust compiler enforces correctness in a myriad of ways that other languages don't. I would much, much rather deal with a legacy Rust codebase than a legacy C++ one.


This isn't true. Or, rather, there are also a myriad of ways that C++ enforces correctness that Rust doesn't.

Legacy codebases are smelly not because of a lack of tools for enforcing correctness. They're smelly because a) programming is hard, and b) a boatload of things are more important than correctness, in the real world.

> I would much, much rather deal with a legacy Rust codebase than a legacy C++

Obviously, because right now legacy Rust codebases are only 5 years old, while legacy C++ codebases are 30+ years old.

But in 20 years it will make no difference. The Rust of 2031 that will need to accommodate 30 years of legacy backwards compatibility will be no prettier than C++ today.


> there are also a myriad of ways that C++ enforces correctness that Rust doesn't.

There are? Like what?

> But in 20 years it will make no difference. The Rust of 2031 that will need to accommodate 30 years of legacy backwards compatibility will be no prettier than C++ today.

I don't really believe this. There are 20 year old Java codebases, and yes they can be a mess and hell to work with, but they're not nearl as bad as C++ ones. And Rust is stricter than Java.


> There are? Like what?

Exceptions, for example.

> There are 20 year old Java codebases, and yes they can be a mess and hell to work with, but they're not nearl as bad as C++ ones.

That's because Java doesn't attempt to solve difficult problems.


Exceptions are terrible for correctness. They introduce hidden control flow paths that developers forget about and fail to handle correctly. (Also they break the "pay for what you use" principle, so C++ is split into two ecosystems, one which uses exceptions and one which doesn't.)

Exceptions are better than magical return values to indicate errors, that's true, but Rust Results achieve the same thing without the downsides of exceptions.

> That's because Java doesn't attempt to solve difficult problems.

Java isn't my favourite language, but that's just silly. GraalVM will do as a counterexample.


> Exceptions are terrible for correctness.

Sometimes they are, and sometimes they are not.

> They introduce hidden control flow paths that developers forget about and fail to handle correctly.

The problem that exceptions solve involve control flow paths that aren't supposed to be handled. Exceptions are not for handling recoverable errors, they are for graceful aborts in a complex, layered and modular program. (E.g., any multi-threaded server, for example.)


> The problem that exceptions solve involve control flow paths that aren't supposed to be handled. Exceptions are not for handling recoverable errors, they are for graceful aborts in a complex, layered and modular program. (E.g., any multi-threaded server, for example.)

For control flow paths that are considered irrecoverable (ie. "this can never happen" branches), Rust has panic!(), which defaults to unwinding the stack, calling Drop implementations (destructors) along the way.

panic!() unwinding only kills the thread it occurs in and Rust's thread-related APIs are designed around preventing data that's been left in an inconsistent state from being observable in other threads without explicitly acknowledging that you're dealing with something like a mutex that's set its "poisoned" flag.

For control flow paths that are considered recoverable, Result<T, E> is basically a way to get checked exceptions which work naturally with higher order functions and have a more concise "call the defined conversion to the specified error return type if necessary, and re-throw" syntax.

If you implement the From/Into interface to define how to convert the error type you received into the error type you're returning, the ? operator will do an "unwrap the Ok value or convert and do an early return of the Err value" in a single character.

A lot of people use the thiserror crate to define their custom error types, which has helpers to makeimplementing the From/Into interface trivial... possibly as trivial as annotating an enum variant with #[from], depending on what you want out of it.

Also, this is from 2005 and chose a "considered harmful" title, but this article makes some good points:

http://www.lighterra.com/papers/exceptionsharmful/


If you restrict exceptions to that case, then Rust has exceptions (panic/catch_unwind). So I'm not sure what your point is.


Fortunately I am mostly able to avoid working on legacy C++ code these days (rr being the exception, and it's only 70K lines).

Rust code accumulates some cruft over time, but Rust's type system and safety guarantees put a floor on how crappy the code can be. The first Rust code I ever wrote is still part of my project five years later; it's less than perfect, but it's free of undefined behavior now just as it was then. The data structure parsers I wrote four years ago look a bit ugly to me now, but are free of exploitable security bugs, and always were. Etc.


> undefined behavior

What does that even mean in the absence of a formal standard and competing compiler implementations? (Hint: nothing.)

If you refuse to define anything then you automatically make the "undefinedness" problem go away. (But not the pain it causes.)

> Rust's type system and safety guarantees put a floor on how crappy the code can be

No. The floor on crappiness is defined by the problem domain. Powerful features allow for powerful takes on crappiness.

Unless you want Rust to stay a teaching language forever, you must necessarily introduce abusable features. (See Python for a real-time slow-motion elaboration of this train wreck if you don't believe me.)


> What does that even mean in the absence of a formal standard and competing compiler implementations? (Hint: nothing.)

Undefined behaviour is stuff that you compiler's optimizers have been promised can never occur, so they are allowed to transform your code based on that assumption.

Here's a post I made on Reddit in 2019 with a list of resources on what undefined behaviour is:

https://www.reddit.com/r/rust/comments/dpxswt/rust_2020_lets...

...and here's the link to an example of what invoking undefined behaviour can do in C++:

https://gcc.godbolt.org/z/0ubnbS

...and here's an explanation of why it does what it does:

https://kristerw.blogspot.com/2017/09/why-undefined-behavior...

(TL;DR: It injects a call to EraseAll because calling Do while it's still null is undefined behaviour, and Do is a static, so the optimizer determines that the only possible answer within the rules it was given is that code outside that compilation unit will have called NeverCalled to set Do = EraseAll before invoking main().)


> What does that even mean in the absence of a formal standard and competing compiler implementations?

It means that when the language designers are asked "what is the behavior of this code (that compiles and doesn't use 'unsafe')?" they never throw up their hands and say "it's undefined behaviour, you must not write that code" (as the C++ definition often says).

They may say "oops, we're not sure what it should do, we need to clarify the language definition and write some tests to ensure the compiler does that". (This happens in C++ too.)

> Powerful features allow for powerful takes on crappiness.

I don't know what this means.

> Unless you want Rust to stay a teaching language forever

A lot of companies big and small are using Rust in production so this is not a compelling premise.


"Undefined behavior" in a C++ context is a legalese feature of the ISO standard. The standard defines, very precisely, the behavior of a compliant C++ compiler. In places where behavior cannot be specified (for logical or practical reasons), the behavior is marked as "undefined".

In absence of a standard effectively every language construct is 'undefined behavior'.

Again, look at Python for a vivid example of this. (We were discussing just this is a neighboring thread: https://news.ycombinator.com/item?id=26826158)

I have zero reason to believe that Rust won't meet Python's fate.

The Rust people don't have a standard and don't understand why they need one; in fact, their lack of standards is somehow touted as a benefit. Apparently, people think that if there is no standard for "defined" and "undefined" behavior that everything is "defined" by default.

(They're absolutely wrong, of course; it's actually the opposite.)


What you seem to be describing is "unspecified behaviour" or "implementation defined behaviour". If your program contains undefined behaviour the compiler makes no guarantees about how your program will behave.

For more see: https://blog.regehr.org/archives/213 and https://raphlinus.github.io/programming/rust/2018/08/17/unde... .


If there is one implementation as the definition then by default all behaviour is defined by the implementation, and hence parent is correct.


No. If we accept your argument then this would be correct:

> In absence of a standard effectively every language construct is 'implementation defined behaviour'.

But the parent said:

> In absence of a standard effectively every language construct is 'undefined behavior'.

Undefined behaviour != implementation defined behaviour. Both of these things exist in C and are separate. You can rely on implementation defined behaviour giving some consistent result on a given, compiler and hardware. You cannot rely on the behaviour of your program if you hit undefined behaviour.


I didn't state 'implementation defined behavior' which encompasses more than the Rust implementation. The Rust implementation defines the behavior, for example signed int overflow.


The "Rust Book" says signed integer overflow to be two's complement in release builds and panic in debug builds: https://doc.rust-lang.org/book/ch03-02-data-types.html

That's clear enough for programmers to rely on. It is nonsense to argue that this is equivalent to C's "the compiler may do anything it wants" just because the "Rust Book" is not called the "Rust Standard" or not "formal" enough.


this magical thinking about standards documents is one of the primary incentives to not have one


It doesn't no, but if I advocate for the language enough and get it used enough, I can guarantee for the entire rest of my career I can maybe avoid ever having to write any more new projects in said languages. I can just avoid jobs where such languages are still used commonly.


Wait ten more years and Rust will become one of these 'said languages'. (Maybe even faster; the Rust people make it their principle to never learn from others' mistakes. There is no way they won't repeat them.)


You're just making stuff up. Most aspects of Rust's design are based on lessons learned from mistakes and good ideas in C++ and other languages. There was a conscious effort to avoid inventing more new things than necessary.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: