Hacker News new | ask | show | jobs
by Jach 3306 days ago
Nim or Rust might be appropriate depending on the project. To me the idea of replacing C with either of them comes down to a value-sell of how much you value Rust's safety guarantees and whether you're willing to make the investment of learning to use it or if you're just going to wrap everything in unsafe{}. Considering you're talking to C programmers, I don't think they value that safety very much. If they did, at least compared to other values, they'd have moved to a GC language like Java long ago, or at least a C++-subset-with-rules. So Nim has a lot of nice features that they might like and expect from modern higher level languages, a lot you can use even if you alloc() everywhere and avoid the GC, which you can do because it has an optional and implementation-swappable GC. And it's not like Nim is just as unsafe as C if you don't use the GC, the compiler stops you from shooting yourself in the foot for a lot of things plus there are some good (tunable) defaults on runtime behavior. Finally the ramp-up time to get productive in Nim, and ongoing development effort, is probably less than Rust, which will help the sell too.
1 comments

pardon my ignorance, but wasn't "safe-and-performant" impossible before rust?

Isn't choosing a GC-language some trading off performance for safety?

It's a common misconception that GC languages must be slower than languages without one. Where that misconception probably comes from is that it's pretty easy to write performant code in C, and hard to write terribly slow code, whereas in languages with GC in some instances you can do as good or better but it's often harder. These days if you're comparing Java and C++, it's not unusual for the Java code to be faster. The bigger tradeoff for GC is more in the loss of determinism, some applications (like games) can't tolerate a random stop-the-world collection for n ms. This can be mitigated by pauseless/stoppable/real-time-guarantees GC implementations though so if your language lets you swap the GC you have options around this. (For something like a game, naive malloc/free aren't enough either. You're going to have to manage your memory intelligently regardless of if you have a GC or not.)

As for Nim all I can say is run some benchmarks on C, Rust, and Nim. Compare the ease of implementation. Compare the relative safeties beyond memory ownership safety. Nim does very well.

You mentioned the determinism tradeoff, which is one the gamedev community talks about endlessly. I'm no low-level expert, but my understanding is that determinism isn't all you lose.

My understanding is that the big tradeoff is against RAM usage. You can have relatively-low-RAM-usage JVM programs, and you can have fast JVM programs, but you usually cannot have both. Keeping the total cost of GC low is done by amortizing the cost over more run-time, which means running less GC often, which means accumulating more garbage between runs. I mean, compared to the insanity that is an Electron app or something, it's no big deal, but this means that the pre-Rust tradeoff is something like "Memory safety, small memory footprint, fast running time: choose two". (I want to emphasize that I'm repeating hearsay, not reporting from my own knowledge.)

Also, it's probably a minor point, but JVM startup time is terrible compared to a native binary. Many many programs don't care about this, but some do.

Code size (not counting the JVM) might well be improved, but if this is the only JVM code on this machine and you need to pay that cost, that's a tradeoff too.

You're right that total memory can be a tradeoff since any GC will necessarily have some space overhead, the question is how much, and whether you can accept some time tradeoff in exchange for less space. So again it all really depends on the GC implementation (or implementations, e.g. standard Java can swap implementations too). Remember that Java was originally designed to run on embedded systems, Lisp has been used on very tiny hardware, and GC theory has been developing from at least the 60s. Memory is plentiful compared to those days.

My go-to reference when getting into GCs is http://gchandbook.org/ and it covers all this in the beginning. One old study tried a particular GC algorithm with Java and some various program benchmarks and found to match perfect manual allocation time (which will be better than in practice) you'll need about 5x the minimum memory, 3x gives you 17% overhead. I think anywhere from 1.5x to 3x is normal in practice since it depends on the application itself. There are tradeoffs everywhere and it's great Rust offers another one.

I thought JVM startup time was a dead horse. :) Sure native beats it, so does Python, but not by much these days.

There were other attempts to bridge safety and performance.

C++11 already gets​ pretty close to that, but Microsoft's M# was used to write a research OS and is probably the closest.

C++, though, only works if you follow a bunch of coding guidelines (that I can tell you for free the last C++ team I worked on didn't) and don't interface with any library APIs that don't follow those guidelines.

Don't get me wrong, C++ is coming on by leaps and bounds, but if you want to get to safe and performant without GC, it's not the place you should be starting. (Equally, I'm not saying Rust solves all the world's problems. Just that it's a better place to start.)

I think it was "safe and performant without a GC" because both Nim and D have C++ level performance while being memory safe as any GCd language is.
You forgot about pony, which is memory safe plus concurrency safe plus faster than C/C++. Nim, D, Rust won't offer that. M# does because it followed the same principles.