Ironically the "simple" JS program has a bug in it. The documentation for fs.watch is very explicit that the filename in the callback can be null and that you need to check for that. In Rust that fact would be encoded in the type system and the programmer would be forced to handle it, but in JS it's easier to just write bad code.
Typescript would require you to check for null before use, which I think makes this a good example of how TS is oftentimes a fairly gentle step up from JS. Or, at least, closer to the correctness of Rust without all the heavy stuff.
You manually manage memory via ownership rules and lifetimes.
You can allocate memory for a Vec with Vec::new. Dropping it deallocates. When and where it's dropped is important. Passing ownership around is important. These are parts of managing the memory.
If you need something to be on the heap, you use Box. If you need something to be shared, you use something like Arc or Rc.
Rust makes it nice to implicitly handle the common cases so you don't feel like you're doing manual memory management, but you're making decisions about that memory management at every step of the way.
To add to this, Java and other GC languages in some sense have manual memory management too, no matter how much we like to pretend otherwise.
It's easy to fall into a trap where your Banana class becomes a GorillaHoldingTheBananaAndTheEntireJungle class(to borrow a phrase from Joe Armstrong), and nothing ever gets freed because everything is always referenced by something else.
Not to mention the dark arts of avoiding long GC pauses etc.
It's possible to do this in rust too, I suppose. The clearest difference is that in rust these things are explicit rather than implicit. To do this in rust you'd have to use 'static, etc. The other distinction is compile-time versus runtime, of course.
> The clearest difference is that in rust these things are explicit rather than implicit. To do this in rust you'd have to use 'static, etc.
You could use 'static, or you can move (partial) ownership of an object into itself with Rc/Arc and locking, causing the underlying counter to never return to 0. It's still very possible to accidentally hold on to a forest.
> It's easy to fall into a trap where your Banana class becomes a GorillaHoldingTheBananaAndTheEntireJungle class(to borrow a phrase from Joe Armstrong), and nothing ever gets freed because everything is always referenced by something else.
Can you elaborate on this? I'm struggling to picture a situation in which I have a gorilla I'm currently using, but keeping the banana it's holding and the jungle it's in alive is a bad thing.
The joke is you're using the banana but you didn't actually want the gorilla, much less the whole jungle. E.g. you might have an object that represents the single database row you're doing something with, but it's keeping alive a big result set and a connection handle and a transaction. The same thing happening with just an in-memory datastructure (e.g. you computed some big tree structure to compute the result you need) is less bad, but it can still impact your memory usage quite a lot.
To extend upon this, memory generally has a single owner. When it goes out of scope, it gets freed [1]. The drop() function, which appears analogous to free() in C/C++, is actually just an empty function who's sole purpose is to take ownership and make it go out of scope, which immediately frees the memory [2].
> This function is not magic; it is literally defined as: pub fn drop<T>(_x: T) {}
This is usually more deterministic than GC languages (no random pauses), but can be less efficient for highly nested data structures. It also makes linked lists impossible without using "unsafe rust", as it doesn't abide by the normal ownership rules.
Linked lists to arbitrary memory, yes. Linked list from a consecutive chunk of memory managed by a bump allocator: just as easy as any language, no need for unsafe.
Admittedly not the easiest language to make a linked list in.
The way I sometimes describe it is that memory is essentially managed at compile time in Rust, whereas with a (tracing) GC it happens at runtime, and in C it's done manually by the programmer. This is a simplification, but it's both true that Rust is similar to C in that there isn't a runtime cost to memory managed and that it's similar to Java in that outside of specific APIs, you have strong guarantees about certain issues with memory not being possible because they're automatically handled by the language.
I half-heartedly tried to make "static memory management" (Rust/C++ RAII) vs "dynamic memory management" (GCs or Rc/Arc in Rust/C++) happen but people generally didn't like it. I do think it's a good framing though.
Yes, by deciding on when, where and how you pass it. You know when it is created and destroyed because you wrote the code that either does that directly, or follows the convention (destroy when out of scope, or use of arc - it is programmer's decision).
False set of options, people just haven't been using the right systems languages with automatic resource management, that also offer escape hatchs to manual memory management.
Cedar being one of the very first ones.
Whose ideas perculated to Modula-2+, Modula-3, Oberon, Oberon-2, Component Pascal, Active Oberon, D, C# (after Midori learnings finally found their way into the language), Swift, Chapel, and whatever comes next where researchers now try to mix both productivity with modern type systems.
JS will immediately error on the lack of parens, but the `in` vs `of` iterates over indexes, not values, and those indexes are unfortunately converted to strings (since `for-in` is object field-name iteration). So even TypeScript would not have caught it when the (stringified) index is used as the first argument of `fs.watch()`.
True, but also the loop syntax is simply incorrect, which would have been caught by running it; so probably a better interpretation is simply that the author didn't spend much time thinking about that JavaScript code because it didn't matter for their point.
But isn’t it at least part of the problem? The JavaScript code looks completely fine, until the HN comment section finds various subtle bugs that sometimes not even TS can protect you against?
no. the JS program has obvious syntax bugs that TS wouldn't compile. commentors above are right that I was just lazy (ty for the callout, I have since fixed the JS program).
Yes. It's coming from the Rust code (where the relevant property is named 'kind') because the author didn't actually run their JS snippet. They've (silently) fixed a number of issues people have pointed out (including the one I pointed out) but they still haven't noticed that one.
https://nodejs.org/api/fs.html#filename-argument