Hacker News new | past | comments | ask | show | jobs | submit login

Absolutely a game changer. I'm using the same stack (minus Diesel) and I love that if it compiles it almost always works. Not so with JS/TS.

With server functions (and of course client/server in the same codebase/language) I also finally get why anyone would be attracted to running JS/TS on the server side.




The only hitch was figuring out how to get server side includes not to break the WASM compilation (the solution was adding #[cfg] and #[cfg_attr] guards around the diesel use and derive rules). Other than that, it just worked - it’s so damn easy!

I always thought WASM would be difficult to use. With Leptos, it’s easier than JavaScript.


> I love that if it compiles it almost always works

I'm going to dig in a bit here: What do you mean specifcally by this? It is quite the claim! I suspect it has to do with meaning there is a low likelyhood of crashing, e.g. no type errors at runtime. My skepticism is that that general statement implies more than this.


Is it because the types and memory issues have been sorted out during the compile phase meaning code will "run" without crashing? That doesn't mean the code will work (infinite loops, etc), but it will run, right?

I've seen more than one Java program that will compile, but will throw a NullPointerError that gets unhandled.


Yes. Quite often you spend hours working on something and when you run it the first time it just works. This is one of the major reasons people fall in love with Rust. There are just very ugly surprises or runtime issues. If it compiles it runs.


Compilation != Correct unless you're Ada spark with probably correct software.

Though it is interesting that a lot of the frustration I had with C++ in my younger years were crashes for unknown reasons.


Certain classes of errors will not appear during runtime if a rust program compiles. This is not the same as saying the program will run correctly. Can you see why?


Of course you’re technically right (you can absolutely write buggy code with Rust) but what others are saying (and what I have also anecdotally observed) is that Rust seems to have a much higher probability of new code just working (bug free) once you’ve cleared the compile step. The only language I’ve used that worked like that was ocaml and maybe Haskell (though I only dabbled in that).


It was quite obvious I was speaking anecdotally.


Say you are sending the data from the back-end to the front-end and you have a time field. With Rust, you are almost 100% certain that you are serializing/deserializing the same type. So if your code compiles, it'll probably work. With JavaScript, there is a good chance that the date String gets misinterpreted somehow and your application misbehaves.

The other advantage: with JavaScript/TypeScript, I find myself frequently getting the output and testing my functions in JavaScript to make sure stuff works. I don't do that when doing Rust -> Rust. If it's a DateTime chrono, the behaviour will be the same.

Also TypeScript is more like type documentation that strong typing enforcement. So it does help but only a little.


There's a type safety mantra that goes "make illegal states unrepresentable" (which came from Yaron Minsky at Jane Street, I believe.) In other words, if you try to represent an illegal program state, the type checker should flag it.

If you design a program's types with this in mind - which requires a fairly powerful type system to do well - that goes a long way to achieving the other commenter's claim.

Related to this is the idea of "static debugging". Following the above approach, the type checker will alert you to many semantic bugs while you're writing the program, without needing to actually run it.

Working through this process tends to help you discover bugs that the type checker alone can't detect. The type system and checker helps you reason about the program's behavior.

The end result of this is that it does indeed tend to seem that "if it compiles, it almost always works."

That's not to say you never experience dynamic logic errors, and it can depend on the kind of program you're writing.

Another way to think about it is that good type systems are the ultimate "shift left" in the software development cycle. They allow you to detect problems about as early as you possibly can. Leveraging that can make a big difference to the SDLC.


In axiomatic safe Rust -- Rust code that doesn't deal with raw pointers -- you won't run into issues like null reference issues unexpectedly because code that can fail will return a type encapsulating the result (ie: Result or Option) that has to have the failure case either explicitly handled, or explicitly discarded with an ".unwrap()"/".expect()" call.

You still have to deal with errors in logic, but it's quite nice to not have to deal with all the other headaches.


Concur. I bring this up because logic errors are the big one (anecdotally), and statements like "If it compiles it's correct" confuse people who haven't used rust, and give them the impression that people saying it are dishonest or exaggerate.


Rust doesn't completely prevent logic errors like it does memory issues, but in practice I find they're much more rare. Rust's type system is very expressive and allows you to check many of your application invariants at compile time (see "Parse, don't validate": https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...). In particular, I'd call out Rust's enums/discriminated unions as being probably the most impactful feature that's missing from many other programming languages.

From a more theoretical perspective, Rust's type system, ownership model, and borrow checker are general-purpose tools you can use to express compile-time-checked application invariants. The language and standard library use them to implement provably safe memory management, but you can also use them to prevent invalid states in your own applications or libraries. My understanding of the history of Rust's development is that the strong type system/ownership/lifetimes came first as a way of preventing logic bugs in complex concurrent applications, and only later the designers realized that system was powerful enough for full memory safety without garbage collection.

When I work with other programming languages, the experience of writing several hundred lines of code, compiling, and have them all work perfectly the first time is rare enough to be a surprise. When I write Rust, it's the norm.

I agree that "if it compiles it's correct" is a sloppy and possibly disingenuous statement. The compiler guarantees that your program is free from memory management errors and type errors; and it gives you the tools to turn most logic errors into type errors; but it does not guarantee absolute freedom from logic errors.




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

Search: