10 ms·
Don't most of the benefits just come down to using a statically typed and thus compiled language? Be it Java, Go or C++; TypeScript is trickier, because it comp
by BinaryIgor 1y ago
Don't most of the benefits just come down to using a statically typed and thus compiled language? Be it Java, Go or C++; TypeScript is trickier, because it compiles to JavaScript and inherits some issues, but it's still fine.
I know that Rust provides some additional compile-time checks because of its stricter type system, but it doesn't come for free - it's harder to learn and arguably to read
- stocksinsmocks 1y agoYes, but more importantly writing a program that compiles in Rust guarantees you a front page spot on HN.
- ViewTrick1002 1y agoNeither Go, Java or C++ would catch that concurrency bug.
- Const-me 1y agoC# would catch the bug at compile time, just like Rust. https://www.rocksolidknowledge.com/articles/locking-asyncawait https://www.rocksolidknowledge.com/articles/locking-asyncawa...
- notfed 1y agoIt's almost as if this post was written in direct response to TFA to brag about how far ahead C# has been for over a decade.
- tialaramex 1y agoThe C# is basically showing the Monitor pattern, which was also a thing in Java last century. The Rust Mutex is an Owning Mutex which is a different feature whose benefit is that you need to take the lock to get at the protected data, which averts situations where you forget in some code but not others and create sync problems - in C# those may go undetected or may trigger a runtime exception, no guarantees. But perhaps even more importantly, and why other languages which could do the Owning Mutex often do not, Rust's borrow checking means the compiler will spot mistakes where you gave back the lock but retained access to the data it was protecting. So you're protected both ways - you can't forget to take the lock, and you also can't give it back without also giving back the access. Monitors prevent only the second (and only partly), an Owning Mutex in most languages prevents the first, but Rust prevents both.
- IshKebab 1y agoI don't know C# but it looks like they added a specific check just for locks, which is far less powerful than Rust's Send/Sync.
- lenkite 1y ago> Neither Go, Java or C++ would catch that concurrency bug. That is incorrect. Java enforces that a monitor lock (or Lock) must be released by the same thread that acquired it. Attempting to unlock from a different thread throws IllegalMonitorStateException.
- rapsey 1y agoAt runtime instead of compile time, which is the point of the thread.
- keybored 1y agoYou ask a question in your first paragraph which you answer in the second.
- pornel 1y agoTo a large extent yes, but Rust adds more dimensions to the type system: ownership, shared vs exclusive access, thread safety, mutually-exclusive fields (sum types). Ownership/borrowing clarifies whether function arguments are given only temporarily to view during the call, or whether they're given to the function to keep and use exclusively. This ensures there won't be any surprise action at distance when the data is mutated, because it's always clear who can do that. In large programs, and when using 3rd party libraries, this is incredibly useful. Compare that to that golang, which has types for slices, but the type system has no opinion on whether data can be appended to a slice or not (what happens depends on capacity at runtime), and you can't lend a slice as a temporary read-only view (without hiding it behind an abstraction that isn't a slice type any more). Thread safety in the type system reliably catches at compile time a class of data race errors that in other languages could be nearly impossible to find and debug, or at very least would require catching at run time under a sanitizer.
- zelphirkalt 1y agoWhat annoys me about borrowing is, that my default mode of operating is to not mutate things if I can avoid it, and I go to some length in avoiding it, but Rust then forces me to copy or clone, to be able to use things, that I won't mutate anyway, after passing them to another procedure. That creates a lot of mental and syntactical overhead. While in an FP language you are passing values and the assumption is already, that you will not mutate things you pass as arguments and as such there is no need to have extra stuff to do, in order to pass things and later still use them. Basically, I don't need ownership, if I don't mutate things. It would be nice to have ownership as a concept, in case I do decide to mutate things, but it sucks to have to pay attention to it, when I don't mutate and to carry that around all the time in the code.
- arnsholt 1y agoOwnership serves another important purpose: it determines when a value is freed.
- zelphirkalt 1y ago
- arwhatever 1y agoI might suspect that if you are lumping all statically-typed languages into a single bucket without making particular distinction among them, then you might not have fully internalized the implications of union (aka Rust enum aka sum) typed data structures combined with exhaustive pattern matching. I like to call it getting "union-pilled" and it's really hard to accept otherwise statically-typed languages once you become familiar.
- JoshTriplett 1y agoOr the fact that Rust's type system includes things like Send and Sync, which aren't tracked and enforced in many otherwise-statically-typed languages. C is statically typed, but its type system tracks much less.
- 1718627440 1y agoMy interpretation is, that C doesn't have data types, but memory layout types.
- gf000 1y agoArguably hardly even that, as memory layout for many in-built types are target and/or compiler-specific.
- ModernMech 1y agoenums + match expressions + tagged unions are the secret sauce of Rust.
- mixmastamyk 1y agoMaybe I need to read it again, but I remember the Rust book saying… you can use enums like C, but what if instead you used them in this more concise way? (Match on members, without the container.) Ok, was able to proceed but don’t feel like I understand what they really are.
- 1y ago
- ModernMech 1y ago> statically typed and thus compiled Statically typed does not imply compiled. You can interpret a statically typed language, for instance. And not every compiled language is all that static. For example, C is statically typed, but also has the ability to play pointer typecasting trickery. So how much can the compiler ever guarantee anything, really? It can't, and we've seen the result is brittle artifacts from C. Rust is statically-typed and it has all kinds of restrictions on what you can do with those types. You can't just pointer cast one thing to another in Rust, that's going to be rejected by the compiler outright. So Rust code has to meet a higher bar of "static" than most languages that call themselves "static". Type casting is just one way Rust does this, other ways have been mentioned. They all add up and the result is Rust artifacts are safter and more secure.
- tialaramex 1y ago> You can't just pointer cast one thing to another in Rust, that's going to be rejected by the compiler You can't safely do this yourself. That is, you couldn't write safe Rust which performs this operation for two arbitrary things. But Rust of course does do this, actually quite a lot, because if we're careful it's entirely safe. That famous Quake 3 Arena "Fast inverse square root" which involves type puns? You can just write that in safe Rust and it'll work fine. You shouldn't - on any even vaguely modern hardware the CPU can do this operation faster anyway - but if you insist it's trivial to write it, just slower. Why can you do that? Well, on all the hardware you'd realistically run Rust on the 32-bit integer types and the 32-bit floating types are the exact same size (duh), same bit order and so on. The CPU does not actually give a shit whether this 32-bit aligned and 32-bit sized value "is" an integer or a floating point number, so "transforming" f32 to u32 or u32 to f32 emits zero CPU instructions, exactly like the rather hairier looking C. So all the Rust standard library has to do is promise that this is OK which on every supported Rust platform it is. If some day they adopted some wheezing 1980s CPU where that can't work they'd have to write custom code for that platform, but so would John Carmack under the same conditions.
- ModernMech 1y ago> because if we're careful it's entirely safe. The thesis of Rust is that in aggregate, everyone can't be careful, therefore allowing anyone to do it (by default) is entirely unsafe. Of course you can do unsafe things in Rust, but relegating that work to the room at the back of the video store labeled "adults only" has the effect of raising code quality for everyone. It turns out if you put up some hoops to jump through before you can access the footguns, people who shouldn't be wielding them don't, and average code quality goes up.
- marcosdumay 1y ago> Don't most of the benefits just come down to using a statically typed and thus compiled language? Doesn't have to be compiled to be statically typed... but yeah, probably. > Be it Java, Go or C++; Lol! No. All static type systems aren't the same. TypeScript would be the only one of your examples that brings the same benefit. But the entire system is broken due to infinite JS Wats it has to be compatible with. > it's harder to learn and arguably to read It's easier to learn it properly, harder to vibe pushing something into it until it seems to works. Granted, vibe pushing code into seemingly working is a huge part of initial learning to code, so yeah, don't pick Rust as your first language. It's absolutely not harder to read.
- adamnemecek 1y agoRust is way more productive than any of the listed languages.
- gf000 1y agoExtraordinary claims require.. at least some kind of evidence. There is very little research comparing PL productivity, because it is very hard to do them properly.
- adamnemecek 1y agoTry it.
- jauntywundrkind 1y agoOne other major factor I'd throw on the heap: traits / implementation traits. They act as both an interface system and as a sort of Extension Method system (as seen in c#). But where-as with interfaces, typically they require you early define what your class implements. Rust gives you a late-bound-ish (still compile time but not defined in the original type) / Inversion of Control way to take whatever you've got and define new things for it. In most languages what types a thing has are defined by the library, but Rust not just allows but is built entirely around taking very simple abstract thing and constructing bigger and bigger toolkits of stuff around them. Very Non-zero sum in ways that languages rarely are. There's a ton of similarity to Extension Methods, where more can get added to the type. But traits / impls are built much more deeply into rust, are how everything works. Extension Methods are also, afaik, just methods, where-as with Rust you really adding new types that an existing defined-elsewhere thing can express. I find it super shocking (and not because duh) that Rust's borrow checking gets all the focus. Because the type system is such a refreshing open ended late-defined reversal of type system dogma, of defining everything ahead of time. It seems like such a superpower of Rust that you can keep adding typiness to a thing, keep expanding what a thing can do. The inversion here is, imo, one of the real largely unseen sources of glory for why Rust keeps succeeding: you don't need to fully consider the entire type system of your program ahead of time, you can layer in typing onto existing types as you please, as fits, as makes sense, and that is a far more dynamic static type system than the same old highly constrained static type dreck we've suffered for decades. Massive break forward: static, but still rather dynamic (at compile time).
- dwaltrip 1y agoAny good articles or blog posts that go deeper on this? Sounds very interesting
- pjmlp 1y agoAn approach that is done in Standard ML with functors.
- gf000 1y agoI mean, is that not just [1] type classes? They are not a new concept, they could be grandparents! [1] not trying to take away anything from the designers, getting it right in combination with all the other features is a huge feat!
- BobbyJo 1y agoIMO, most of the terse syntax in Rust comes from the sugar they've added for error handling.
- lmm 1y ago> Don't most of the benefits just come down to using a statically typed and thus compiled language? Be it Java, Go or C++; TypeScript is trickier, because it compiles to JavaScript and inherits some issues, but it's still fine. No. You have to have a certain amount of basic functionality in your type system; in particular, sum types, which surprisingly many languages still lack. (Note that static typing does not require compilation or vice versa) > I know that Rust provides some additional compile-time checks because of its stricter type system, but it doesn't come for free - it's harder to learn and arguably to read ML-family languages are generally easier to learn and read if you start from them. It's just familiarity.
- rvz 1y ago> Don't most of the benefits just come down to using a statically typed and thus compiled language? Be it Java, Go or C++; TypeScript is trickier, because it compiles to JavaScript and inherits some issues, but it's still fine. Yes. The type systems of these modern compiled languages are more sound than anything that Javascript and Typescript can ever provide. Anyone using such languages that have a totally weak type system and a dynamic typing system as well is going to run into hundreds of headaches - hence why they love properly typed-systems such as Rust which actually is a well designed language.
- saghm 1y agoYes, all four of them will have some checks that won't be present in a dynamic language, but the differences between them are large enough to be significant. Riding a bike and driving a car are both much faster than going on foot, but if you only view this as a "benefit that comes down to using wheels", you're missing some pretty meaningful details.
- eptcyka 1y agoThe specific example used wouldn’t be caught by C++ or Java.
- gf000 1y agoThe redirect one would funnily be caught in Java. For the simple reason of not having properties, it would have to be a method call, where the author's wrong assumption would no longer apply.
- rendaw 1y agoNot all static type systems are equally expressive/safe/consistent - Java falls back to `Object` and runtime casts frequently, Go doesn't have enums, and C++ variants come with significant footguns and ergonomics issues since they were hacked on and aren't first class language features (i.e. safe access requires using `try/except` which is exclusive with other control structures).
- gf000 1y ago> Java falls back to `Object` and runtime casts frequently Is it frequently? Generics are definitely not as nice as they could be, but they are surprisingly "sufficient" for almost any library, e.g. a full on type-safe SQL DSL like JOOQ. Unsafe casts are very rare, and where you do need Object and stuff are very dynamic stuff where it's impossible to extend compile-time guarantees even theoretically (e.g. runtime code generation, dynamic code loading, etc - people often forget about these use cases, not everything can work in a closed universe)
- lukaseder 1y agojOOQ's internals are full of unsafe casts, though.
- gf000 1y agoWell, you have basically implemented a Java type-system level type checker for SQL. I don't believe there is any type system strong enough to express the whole thing without the escape hatches (casts), besides Lean, Coq and alia.
- lukaseder 1y agoNot sure what you mean. I meant that without declaration site variance, ("pragmatic") unsafe casting is everywhere in jOOQ's internals. Without being able to capture wildcards in local type variables, ("pragmatic") rawtypes are everywhere as well (check Collections.swap() for an illustration)
- csomar 1y agoYes. What Rust adds is a better type system. Statically typed is as good as your Type system (which is why TypeScript still sucks). The concurrency/safety/memory story is only valid in a few rare cases and I wish people didn't try to sell Rust for these features.
- akkad33 1y agoNo. Other languages don't prevent concurrency related bugs like the one in the article. Rust has reference aliasing rules and lifetimes and send and sync traits. These things do not exist in Java and others, so will not prevent such bugs