22 ms·
Unexpected productivity boost of Rust
- koakuma-chan 1y agoI encourage every one to at least stop writing code in Python.
- jvanderbot 1y agoThe benefits realized can be mostly attributed to strong type checking. I'm a rust dev full time. And I agree with everything here. But I also want people to realize it's not "Just Rust" that does this. In case anyone gets FOMO.
- koakuma-chan 1y agoDo you know a language other than Rust that has alternative for docs.rs? In JavaScript and Python they never bother to have any documentation or reference, one notable example that gets me frustrated these days is the openai SDK for TypeScript. There is no documentation for it. I have to go look at the source code to figure out what the hell goes on.
- 9question1 1y agoThese days isn't the solution to this just "ask <insert LLM of choice here>" to read the code and write the documentation"?
- koakuma-chan 1y agoYes, you can have Claude Code go through the code and make an .md file with documentation for all the public APIs. I do that for everything that doesn't provide llms.txt.
- bigstrat2003 1y agoIf you want to not reliably know anything about the code, sure. But if you want to have useful knowledge, using a stochastically unreliable tool isn't going to cut it.
- cbm-vic-20 1y agojavadoc.io is similar to (but not as good as) docs.rs for the Java ecosystem. It pulls the JavaDoc out of packages that are published to the Maven Central repository. It doesn't have good discoverability like docs.rs, though, and it's dependent on the publishers actually including the javadoc files.
- love2read 1y agojavadoc.io is similar in the fact that it lists things I guess? It looks worse (objective) but is also depended on a lot less so is just by default going to be a second thought. It’s also not auto-generated like docs.rs like you said.
- wolvesechoes 1y ago> In JavaScript and Python they never bother to have any documentation or reference Please, main Python libraries have much richer docs than almost anything from Rust ecosystem.
- veber-alex 1y agoHere is some actual useful advice: Use a type checker! Pyright can get you like 80% of Rust's type safety.
- kragen 1y agoWhat are the major pros and cons of Pyright and Mypy relative to one another?
- koakuma-chan 1y agoI don't agree that Python can be saved by a type checker. The language itself is flawed irreversibly, as well as its ecosystem. It's a big big mess. Can't build reliable software in Python.
- dkdcio 1y agoyou can and this is a juvenile position. there is reliable software in any language as popular and widespread as Python. every language is flawed
- IshKebab 1y ago> every language is flawed But not equally flawed. https://www.lesswrong.com/posts/dLJv2CoRCgeC2mPgj/the-fallacy-of-gray https://www.lesswrong.com/posts/dLJv2CoRCgeC2mPgj/the-fallac...
- grep_it 1y agoIs this rage-bait? A language alone doesn't dictate reliability. There are tons of large scale systems out there running on Python. As for the language being, "flawed irreversibly", I'd be curious to hear you expand on that with examples.
- nicce 1y agoAll programming languages are Turing complete and you can arque with that. But you reach similar results with varying pre-knowledge and effort. Sometimes it is even impossible. All programs can be bug-free but most are not, if we reverse the argument.
- nilslindemann 1y agoPeople who recommend that other people stop using one of the best documented languages on the planet with a huge library ecosystem, a friendly user base, a clean syntax, excellent reference documentation, intuitive function names, readable tracebacks, superb editor and AI support.
- love2read 1y agowhat language are you referring to here?
- nilslindemann 1y agoPython. https://www.tiobe.com/tiobe-index/ https://www.tiobe.com/tiobe-index/ https://pypl.github.io/PYPL.html https://pypl.github.io/PYPL.html https://www.statista.com/statistics/793628/worldwide-developer-survey-most-used-languages/ https://www.statista.com/statistics/793628/worldwide-develop... https://redmonk.com/sogrady/2024/03/08/language-rankings-1-24/ https://redmonk.com/sogrady/2024/03/08/language-rankings-1-2...
- ironmagma 1y agoIntuitive function names like __new__() and __init__()? Or id() and pickle.dumps()? The accessibility of Python is overrated. It's a language with warts and issues just like the others. Also the lack of static typing is a real hindrance (yes I know about mypy).
- mixmastamyk 1y agoDunders keep them out of the public namespace on purpose.
- frabcus 1y agoIt doesn't solve all the problems at library boundaries, but pyright is fairly new and vastly vastly better than mypy. With it Python feels about at the type safety level of Typescript - not as good as a language that had types the whole time, but much much better than nothing if enforced with strict rules in CI.
- lynndotpy 1y agoPythons power is in the REPL. It's good for when you want to do something ad-hoc that's too much for Bash or a graphing calculator. It has a large enough ecosystem that you can make a PoC for basically anything in a day. It's really, really good for <1000 LoC day projects that you won't be maintaining. (And, if you're writing entirely in the REPL, you probably won't even be saving the code in the first place.)
- love2read 1y agothe problem is that when you write enough <1kloc projects, a couple of them are useful enough to stay used in which case you are now maintaining python in prod.
- hnaccount_rng 1y agoYes but the alternative is "not having those projects at all" not "having them in a 'better' language"
- love2read 1y agoOf course not, the alternate is writing these projects in a more maintainable language
- noisy_boy 1y agoOr writing them in whatever language gets you going quickly and rewrite the "successful" ones in a more safe language like Rust. Of course write test cases for those ones first and then use LLM help to migrate them, including tests, to a safer language. Problem happens when the velocity of changes is too much and/or there is no appetite for migration - those are a different category of problem. The ideal scenario is what you are saying but most of the time it boils down to deadline vs familiarity/skill (of the developer and the team) trade-off.
- lynndotpy 1y agoI don't think that's inevitable, you can just not do that. I've been doing this for 15 years and it hasn't happened to me.
- voidUpdate 1y agoWhat language do you recommend instead for the type of small tasks I write python to solve? Needs to have a good image manipulation library, tools for manipulating binary data and converting it to normal types, good list comprehension tools, the ability to easily be a web client or server and a good REPL
- koakuma-chan 1y agoBun
- voidUpdate 1y agoisnt that just javascript?
- koakuma-chan 1y agoIt's JavaScript (TypeScript) that ships as a single executable with lots of stuff in it like bundler, compiler, postgres client, etc. I use it for scripting, e.g., seed database, etc.
- lmm 1y agoIf you're willing to put a bunch of up-front effort into learning it first, Haskell. If not, OCaml.
- Spivak 1y agoHow do you encode the locking issue in the type system, it seems magical? Can you just never hold any locks when calling await, is it smart enough to know that this scheduler might move work between threads?
- jvanderbot 1y agoYes Rust has ways of verifying single access to locks through the borrow checker and lifetimes.
- Spivak 1y agoWell yes but this seems more complicated. If the code was executed on a single thread it would be correct. The compiler somehow knows that await might move the work to another thread in which the code would still be correct if it weren't for the pesky undefined behavior. At all points it seems like it would be single access and yet it catches this.
- 0x1ceb00da 1y agoAn async function call returns a future which is an object holding the state of the async computation. Multithreaded runtimes like tokio require this future to be `Send`, which means it can be moved from one thread to another. The future generated by the compiler is not Send if there is a local variable crossing an await boundary that is not `Send`.
- ViewTrick1002 1y agoThat comes from the where the tokio task is spawned annotating the future with send. https://docs.rs/tokio/latest/tokio/task/fn.spawn.html https://docs.rs/tokio/latest/tokio/task/fn.spawn.html If you want to run everything on the same thread then localset enables that. See how the spawn function does not include the send bound. https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html
- veber-alex 1y ago
- raphinou 1y agoThough it's not the only benefit, I enjoy rust and fsharp's typesystems most when refactoring code. Fearless refactoring is the right expression here.
- estebank 1y agoThe only issue with it is that Rust's aversion to half-baked code means that you can't have "partially working code" during the refactor: you either finish it or bail on it, without the possibility to have inconsistent codebase state. This is particularly annoying for exploratory code. On the other hand, that strictness is precisely what leads people to end up with generally reasonable code.
- tialaramex 1y agoI find a healthy dose of todo!() is excellent for this. match foo { (3...=5, x, BLABLABLA) => easy(x), _ => todo!("I should actually implement this for non-trivial cases"), } The nice thing about todo!() is that it type checks, obviously it always diverges so the type match is trivial, but it means this compiles and, so long as we don't cause the non-trivial case to happen, it even works at runtime.
- estebank 1y agoThe thing is I want an equivalent to `todo!()` for the type-system. A mode of operation where if you have some compile errors, you can still run tests. Like for example, if you have fn foo() -> impl Display { NotDisplay::new() } and a test references `foo`, then it gets replaced for the purposes of the test with fn foo() -> impl Display { panic!("`NotDisplay` doesn't `impl Display`") } This should not be part of the language, but part of the toolchain. Same thing for the borrow-checker.
- tialaramex 1y agoSo maybe a proc macro which lets you #[dummy(Clone,Eq,PartialEq)] instead of #[derive(Clone,Eq,PartialEq)] ? Although you said "mode of operation" and I can't get behind that idea, I think the choice to just wrap overflow by default for the integer types in release builds was probably a mistake. It's good that I can turn it off, but it shouldn't have been the default.
- pjmlp 1y agoMost of these productivity gains are achievable in any Standard ML influenced type system.
- bryanlarsen 1y agoThe main difference between Rust and other languages with a Standard ML influenced type system is that Rust has features that can let you get executive sign off for switching languages.
- binary132 1y agowhat is the main advantage of Rust over OCaml for most applications in this respect?
- maleldil 1y agoEcosystem. It's much easier to find libraries for Rust.
- pjmlp 1y agoopam has plenty of libraries, we should not mix quantity with quality. Also we don't need leftpad like libraries, which is the direction Rust seems to be going when I try some some basic application that has like 50 crate dependencies.
- binary132 1y agoRust is very difficult for most people, so do you think that this advantage outweighs that disadvantage? Is that really the only meaningful advantage if you’re not writing a driver or internet-facing compressor at scale?
- maleldil 1y agoYou're greatly overrating how difficult Rust is. The wealth of high-quality software being written in it shows many people think it's worth the effort.
- loeg 1y agoThe first save is from a failure of Rust's own making: async Rust. It's an awful footgun of a concept that is made vaguely bearable by the rest of the type system.
- love2read 1y ago(but is still one of the most capable languages for async programming)
- loeg 1y agoMaybe. I think async as a concept is kind of bad in general, but if you want it, GC makes it more ergonomic.
- IshKebab 1y agoI think one of the most useful places for async is embedded - Embassy is amazing & you can't use GC there. Rust async almost seems like it was designed more for that than for the high level web stuff that lots of people use it for. They definitely could have made it more ergonomic though. Pin is super confusing and there are a disappointing number of footguns, e.g. it's very easy to mess up loop/select and that's super common.
- veber-alex 1y agoI find the Zig example to be shocking. It's just so brittle. How can anyone think this is a good idea?
- zparky 1y agoI didn't even see the error after the first glance until I read your comment.
- veber-alex 1y agoYeah, I had to do a double take while reading the article because I missed the error completely while looking at the code.
- WD-42 1y agoI saw this comment first and then read the zig and still couldn’t find the error until I read the explanation below it. This has to be a bug.
- tialaramex 1y agoI assume it's a bug. However because this is an auteur language if you want the bug fixed it will be important to ensure the auteur also thinks it is a bug. If they get it into their head that it's supposed to be like this, they'll double down and regardless of how many people are annoyed they're insist on it.
- veber-alex 1y agoKnowing how the Zig developers operate it's 100% not a bug and it's exactly the case you described.
- ozgrakkurt 1y agoTbh you would just use ErrorTypeName.ErrorKind to check equality and not error.ErrorKind if you are worried about this. It is a tradeoff between making some things easier. And probably compiler is not mature enough to catch this mistake yet but it will be at some point. Zig being an auteur language is a very good thing from my perspective, for example you get this new IO approach which is amazing and probably wouldn’t happen if Andrew Kelley wasn’t in the position he is in. I have been using Rust to write storage engines past couple years and it’s async and io systems have many performance mistakes. Whole ecosystem feels like it is basically designed for writing web servers. An example is a file format library using Io traits everywhere and using buffered versions for w/e reason. Then you get a couple extra memcopy calls that are copying huge buffers. Combined with global allocation everywhere approach, it generates a lot of page faults which tanks performance. Another example was, file format and just compute libraries using asyncio traits everywhere which forces everything to be send+sync+’static which makes it basically impossible to use in single thread context with local allocators. Another example is a library using vec everywhere even if they know what size they’ll need and generating memcopies as vec is getting bigger. Language just makes it too easy. I’m not saying Rust is bad, it is a super productive ecosystem. But it is good that Zig is able to go deeper and enable more things. Which is possible because one guy can just say “I’ll break the entire IO API so I can make it better”.
- 0x1ceb00da 1y agoThe typescript/javascript example is a little dishonest. Nothing will save you if you don't know how the runtime/environment/domain semantics work.
- 9question1 1y agoI love Typescript but I think I disagree with this. The point of the post seems to be that features of the Rust compiler help enforce that you use certain runtime / environment / domain semantics in ways that eliminate common classes of errors. That's never going to prevent all errors, but preventing large groups of common errors so that you only have to manually remember a smaller set of runtime/environment/domain semantics could have some value.
- 0x1ceb00da 1y agoIt isn't typescript's fault. Borrow checker won't save you from bugs in your SQL queries that you send to the DBMS. Typescript doesn't care about the browser just like rust doesn't care about SQL
- bryanlarsen 1y agoSeaORM and similar crates in Rust will catch some common SQL bugs through the type system.
- morcus 1y agoTypescript also has ORMs. The problem is not with TypeScript or even JavaScript but an odd Browser API where mutating some random value of an object results in a redirect on the page, but not synchronously. Even if the language of the browser were Rust, there's nothing about the type system specifically that would have caught this bug (as far as I can tell, anyways. Presumably there's something in the background periodically reading the value of `href` and updating the page accordingly, but since that background job only would have needed read and not write access to the variable, I don't think the borrow checker would have helped here)
- aeve890 1y ago>The code will compile just fine. The Zig compiler will generate a new number for each unique 'error.*' This is wild. I assume there's at least the tooling to catch this kind of errors right?
- TUSF 1y agoThere's no special tooling to catch this, because nobody catches an error with if-else—it's simply not idiomatic. Everyone uses switch statements in the catch block, and I've never seen anybody using anything other than switch, when catching an error.
- veber-alex 1y agoBut why? If I just need to check for 1 specific error and do something why do I need a switch? In Rust you have both "match" (like switch) and "if let" which just pattern matches one variant but both are properly checked by the compiler to have only valid values.
- arwalk 1y agoThe real problem is not about the if-else, its that he's comparing to the global error set, and not to the FileError error set he created specifically to define AccessDenied.
- love2read 1y agoThe real problem is that this compiles without error
- love2read 1y ago“There’s no specific tooling to catch this, because nobody[…]”. So? This is a standard library/language feature, which is usually the first place people go for features in the language. To say that nobody uses it is stupid.
- dminik 1y agoBoth the Zig standard library as well as several third party projects do check errors like this. I already commented on Zig compiler/stdlib code itself, but here's Tigerbeetle and Bun, the two biggest(?) Zig codebases: https://github.com/search?q=repo%3Atigerbeetle%2Ftigerbeetle%20%22%3D%3D%20error.%22&type=code https://github.com/search?q=repo%3Atigerbeetle%2Ftigerbeetle... https://github.com/search?q=repo%3Aoven-sh%2Fbun%20%22%3D%3D%20error.%22&type=code https://github.com/search?q=repo%3Aoven-sh%2Fbun%20%22%3D%3D...
- whazor 1y agoI agree with the premise of more sophisticated compilers give you a productivity boost. But compiling with cargo can be very slow, and having a strong workstation can be worth it.
- gedy 1y ago> Assigning a value to 'window.location.href' doesn't immediately redirect you, like I thought it would. That's not a "Typescript" or language issue, that's a DOM/browser API weirdness
- gavmor 1y agoJeeze, why should attribute assignment have side-effects, anyway? That'd be gross!
- scheme271 1y agoI think it's unavoidable for any sufficiently complicated object or variable. Think of c++'s unique_ptr that guarantees that only a single reference to memory exists so that that the memory can be free'd safely when it goes out of scope. Or thing of a complicated object that has references to allocated memory. You either allocate a bunch of memory and copy values over or have two objects refer to the same bit of allocated memory. Each solution potentially has major downsides. Your objects don't even have to be all that exotic, think of a vector holding 1024 fp64 values or a 8kb long string.
- gavmor 1y agoYikes, sharing the same bit of memory seems like a Demeter violation! Is it really all that common? Maybe I am too OOP-pilled to grok the sense of it.
- love2read 1y ago*though it could have been fixed by typescript had they cared.
- BinaryIgor 1y agoDon'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.
- ModernMech 1y agoI had to explain this to my students today using XKCD #1987, that although Python is considered a universally "simple" and "easy to use" language, a lot of the complexity in that ecosystem is paid for on the backend. With Rust, they frontload the complexity, so it's considered to be "hard to learn". But I've got to say, Rust's "complexities" have allowed me to build a taller software tower than I've ever been able to build before in any other language I've used professionally (C/C++/Java/Swift/Javascript/Python). And that's the thing a lot of people don't get about Rust, because you can only really appreciate it once you've climbed the steep learning curve. At this point I've gone through several risky and time-consuming (weeks) refactors of a substantial Rust codebase, and every time it's worked out I'm amazed it wasn't the kind of disaster I've experienced refactoring in other languages, where the refactor has to be abandoned because it got so hairy and everyone lost all hope and motivation. They don't tell you about that kind of pain in the Python tutorial when they talk about how easy it is to not have to type curly braces and have dynamic types everywhere. And you don't really find that pleasure in Rust until you've built enough experience and code to have to do a substantial refactor. So I can understand why the Rust value proposition is dubious for people who are new to the language and programming in general. [1] https://xkcd.com/1987 https://xkcd.com/1987
- lmm 1y ago> I had to explain this to my students today using XKCD #1987, that although Python is considered a universally "simple" and "easy to use" language, a lot of the complexity in that ecosystem is paid for on the backend. No, that's just an unrelated coincidence. Python happens to be a good language with awful tooling, but there are also good languages with good tooling and awful languages with awful tooling.
- merdaverse 1y agoCode written below your line gets executed if you don't return early. More breaking news at 8. Seriously, why would you think that assigning a value would stop your script from executing? Maybe the Typescript example is missing some context, but it seems like such a weird case to present as a "data race".
- love2read 1y agoIt seems weird to shame someone for talking about their own experience?
- lights0123 1y agoexit(), execve(), and friends do immediately stop execution—I could understand why you'd think a redirect would as well.
- JoshTriplett 1y agoExactly. Given that JavaScript runs in the context of a page, redirecting off of the page seems like it should act like a "noreturn" function...but it doesn't. That seems like a very easy mistake to make.
- dvt 1y agoThe redirect is an assignment. In no language has a variable assignment ever stopped execution.
- dminik 1y agoThat doesn't seem that obvious to me. You could have a setter that just calls exit and terminates the whole program.
- dvt 1y agoYeah, this is actually a good point, could have a custom setter theoretically that simply looks like assignment, but does some fancy logic. const location = { set current(where) { if (where == "boom") { throw new Error("Uh oh"); // Control flow breaks here } } }; location.current = "boom" // exits control flow, though it looks like assignment, JS is dumb lol
- antirez 1y agoI always thought that the POSIX threads semantics that forces the thread acquiring the lock to be the same thread that releases it, is too strict and not needed. In certain use cases it forces you to redesign the code in more complicated ways.
- murderfs 1y agoIt isn't too strict. Releasing a pthread_mutex has the semantics of being a release memory barrier, which means that any writes on that thread will be visible by other threads that issue a subsequent acquire memory barrier (e.g. by acquiring the mutex). If you want this behavior, it's relatively simple to implement your own mutex on top of futex, but no one is going to expect the behavior it provides.
- matklad 1y agoI used to think that way, but I've learned that there's one good reason for why the API is designed that way: priority inheritance. Priorities are bound to threads, and, when a high priority threads wants to lock a currently occupied mutex, we want to bump the priority of the current holder. And POSIX requirement makes that easy --- the thread who locked the mutex must be the one holding it. Related: https://man7.org/linux/man-pages/man2/futex.2.html#:~:text=%20Priority-inheritance https://man7.org/linux/man-pages/man2/futex.2.html#:~:text=%...
- dvt 1y agoUsing sync locks in an async environment is a code smell, so the Rust code is bad right off the bat. Just stick with async locks and take the tiny performance hit unless you know exactly what you're doing.
- ozgrakkurt 1y agoIf I remember correctly tokio Mutex docs mentions you should use std Mutex unless you are holding the lock across an await
- deleted 1y ago[deleted]
- pwdisswordfishz 1y agoWhich is exactly what happens here!
- ozgrakkurt 1y agoThat section looks like a cut out version of a pseudo code more than actual code. He could have mem::dropped the lock 50 lines before the await point
- bkolobara 1y agoI didn't need to hold the lock across the .await point and in that case the official docs recommend a regular lock.
- pkolaczk 1y agoRust is one of a very few languages where I can make a total mess in my code by refactoring, have like 100+ compilation errors in multiple modules, think I’m never going to fix it, yet at the end of the day I eventually get everything to compile fine, I run the tests and… they all pass from the first go. All green. Hard to believe but it happened many times to me.
- ChadNauseam 1y agoSame. I often have the experience of seeing my app suddenly work again and thinking "wait... I'm done?"
- jemiluv8 1y agoThe Typescript example given is an obvious bug to me even before I knew how the window redirect thing works. It was an obvious control flow issue And I find the event loop vrs concurrency via mutexes to be like an apples to oranges comparison. They both do some form of concurrency but not nearly in the same way Again, Typescript is hardly considered a language. It is just a tool used to keep javascript under some control on large projects. Again, the comparison between rust and Typescript on a language level is not a great match. As for fearless refactoring, don't get me started. I experienced this the first time I was porting a vanilla js backend to a typescript version. It was awesome. I won't say it works in much the same way as rust does but man, if you ever ported a rest api written in javascript to Typescript - you'd experience a similar effect.
- jmull 1y agoThe javascript browser bug has nothing to do with the language. It’s just a logic bug. E.g., the code doesn’t match their own English description of the logic: “If yes, redirect to the specific page. If not, go to the dashboard or onboarding page.” The code is missing the “if not” (probably best expressed using an “else” clause following the if block).
- notfed 1y agoI wouldn't say nothing. I mean, we're essentially talking about a global variable that's specified into the language.
- pwdisswordfishz 1y agoWhere exactly? https://tc39.es/ecma262/multipage/ https://tc39.es/ecma262/multipage/
- jmull 1y agowindow and window.location aren't part of javascript. They are part of the javascript api that standard browsers provide. It's fair to point out that browser api is confusing. You might not think of setting a property as kicking off an asynchronous operation, especially if it seems to has instantaneous effect at first. But the basic control flow logic of that code is wrong. Confusion about whether a side-effect from an api call might bail you out from your error is beside the point.
- byw 1y agoI'm curious if Rust (WASM through JS interop?) also has access to `window.location`, and if the API has some more safety mechanisms.
- steveklabnik 1y agoweb_sys lets you call window.location, which returns a Location, which has href and set_href methods. They do the same thing the native APIs do, as that is their purpose.
- marcosdumay 1y agoOn that graph, both curves should go monotonically down, just one faster than the other.
- jaccola 1y agoI would love to see a web app with an interface where I select two programming languages and it shows me a really clean snippet of code in language A and says "Look how terribly hard and unclean this is to achieve in language B". (and then you could reverse to see where B outshines A). Languages are a collection of tradeoffs so I'm pretty sure you could find examples for every two languages in existence. It also makes these kinds of comparisons ~useless.
- jibal 1y agoActually, these sorts of comparisons are extremely useful, and an argument based on imagining an unknown case that makes the argument work is fallacious.
- prerok 1y agoFor one, snippets is exactly where you don't get good language comparisons. For example, python is AFAIK the lead language to get something done quickly. At least as per AoC leaderboards. It's a horrible language to have in production though (experienced it with 700k+ LOC). Rust is also ok to do for AoC, but you will (based on stats I saw) need about 2x time to implement. Which in production software is definitely worth it, because of less cost in fixing stupid mistakes, but a code snippet will not show you that.
- munchler 1y agoIf I might tweak your point a bit: Snippets are fine for showing language differences when programming in the small, but not for programming in the large. https://en.wikipedia.org/wiki/Programming_in_the_large_and_programming_in_the_small https://en.wikipedia.org/wiki/Programming_in_the_large_and_p...
- guywithabike 1y agoRosetta Code is what you're looking for, I believe: https://rosettacode.org/ https://rosettacode.org/
- ChadNauseam 1y agoThis post mirrors my experience 100%. The effect is even stronger when there are multiple people involved on a project, or when you're maintaining a project that you didn't start. There, the benefits of static typing and domain modelling with ADTs become essential. Take take GHC - a haskell project, not a rust one, but it's been under active development by many different people since 1990 and is still going strong. Is there any doubt that the guarantees provided by Haskell's type system were beneficial to making this possible? Not to say that it's strictly necessary – the linux kernel is an even more impressive project and it's in C – but something that is not necessary for success can still increase your odds of success. You can ask any professional python programmer how much time they've spent trying to figure out the methods that are callable on the object returned by some pytorch function, and they will all tell you it's a challenge that occurs at least weekly. You can ask any C++ programmer how much time they've spent debugging segfaults. You can ask any java programmer how much time they've spent debugging null pointer exceptions. These are all common problems that waste an incredible amount of time, that simply do not occur to anywhere close to the same extent in Rust. It's true that you can get some of these benefits by writing tests. But would tests have prevented the issue that OP mentioned in his post, where acquiring a mutex from one thread and releasing it from another is undefined? It's highly doubtful, unless you have some kind of intensive fuzz-testing infrastructure that everyone talks about and no one seems to actually have. And what is more time-efficient: setting up that infrastructure, running it, seeing that it detects undefined behavior at the point of the mutex being released, and realizing that it happened because the mutex was sent to a different thread? Or simply getting a compile error the moment you write the code that says "hey pal, mutex guards can't be moved to a different thread". Plus, everyone who's worked on a codebase with a lot of tests can tell you that you sometimes end up spending more time fixing tests than you do actually writing code. For whatever reason, I spend much less time fixing types than fixing tests. There is a compounding benefit as well. When you can refactor easily (and unit tests often do not make refactoring much easier...), you can iterate on your code's architecture until you find one that meshes naturally with your domain. And when your requirements change and your domain evolves, you can refactor again. If refactoring is too expensive to attempt, your architecture will become more and more out-of-sync with your domain until your codebase is unmaintainable spaghetti. If you imagine a simple model where every new requirement either forces you into refactoring your code or spaghettifying your code, and assume that each instance of spaghettification induces a 1% dev speed slowdown, you can see that these refactors become basically essential. Because 100 new requirements in the future, the spaghetti coder will be operating at 36% the productivity of the counterfactual person who did all the refactors. Seen this way, it's clear that you have to do the refactors, and then a major component of productivity is whether you can do them quickly. An area where it's widely agreed rust excels at. There are plenty of places we can look at Rust and find ourselves wanting more. But that doesn't mean we shouldn't be proud of what Rust has accomplished. It has finally brought many of the innovations of ML and Haskell to the masses, and innovated new type-system features on top of that, leading to a very productive and pleasantly-designed language. (I also left this comment on reddit, and am copying it here.)
- tomovo 1y agoThe graph has no units or scale. What is the project size at the crossover point? The article says "certain size" which is unclear.
- deleted 1y ago[deleted]
- nneonneo 1y agoLast year I ported a virtio-host network driver written in Rust, entirely changing its backend, interrupt mechanism and converting it from a library to a standalone process. This is a complex program that handles low-level memory mappings, VM interrupts, network sockets, multithreading, etc. What’s remarkable is that (a) I have very little Rust experience overall (mostly a Python programmer), (b) very little virtio experience, and (c) essentially no experience working with any of the libraries involved. Yet, I pulled off the refactor inside of a week, because by the time the project actually compiled, it worked perfectly (with one minor Drop-related bug that was easily found and fixed). This was aided by libraries that went out of their way to make sure you couldn’t hold them wrong, and it shows.
- mmastrac 1y agoI've been writing Rust code for a while and generally if it compiles, it works. There are occasional deadlocks and higher-level ordering issues from time-to-time, but modulo bugs, the compiler succeeding generally means it'll run a decent amount of your project.
- jabwd 1y agoThough my code complexity is far FAR from what you've been writing it has been a similar experience for me. There are a few footguns and a bug in chrono I still have to find the energy to report or fix; which has been causing a bi-yearly issue, but apart from that happy lil programmer.
- burntsushi 1y agoI would be curious to know if you've looked at `jiff`. Does it resolve your bug/footgun issues with chrono?
- shark1 1y agoReviews like yours are increasing my interested towards Rust. Apparently the tools and ecosystem are great, build on solid concepts and foundation.
- torginus 1y agoThe Rust example that you point out might not trigger compile time warnings in other languages, but most OS mutexes (certainly the ones in Windows and Linux) will complain if you release them on a different thread than you acquired them.
- IshKebab 1y agoYou've misunderstood somehow. The point is that Rust detects the mistake at compile time so you don't get to those runtime errors (which might be very rare or context-dependent).
- neandrake 1y agoRust caught the lock being held across an await boundary, but without further context I'd hedge there's still a concurrency issue there if the solution was to release the lock before the await. Presumably the lock is intended to be used for blocking until the commit is created, which would only be guaranteed after the await. Releasing the lock after submitting the transaction to the database but before getting confirmation that it completed successfully would probably result in further edge cases. I'm unfamiliar with rust's async, but is there a join/select that should be used to block, after which the lock should be unlocked?
- Cyph0n 1y agoYou can use a async-aware mutex if you require it to be held across await points. For example, if using the Tokio runtime: https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html.
- lilyball 1y agoIf you need to hold a lock across an await point, you can switch to an async-aware mutex. Both the futures crate and the tokio crate have implementations of async-aware mutexes. You usually only want this if you're holding it across an await point because they're more expensive than blocking mutexes (the other reason to use this is if you expect the mutex to be held for a significant amount of time, so an async-aware lock will allow other tasks to progress while waiting to take the lock).
- suriya-ganesh 1y agoI vibe with this entirely. Rust to me makes a lot more sense. The compiler gives reasonable errors, the code structure is clean and it's obvious where everything should go. I just can't deal with Typescript at all. There is a sense of uncertainty in TypeScript that is just unbearable. It is really hard to be confident about the code.
- munchler 1y ago> I have found that Rust's strong safety guarantees give me much more confidence when touching the codebase. With that extra confidence I'm much more willing to refactor even critical parts of the app, which has a very positive effect on my productivity, and long-term maintainability. That's great, but the graph at the top shows your productivity more than doubling as the size of the project increases, which seems very dubious. Perhaps this is just intended as visual hyperbole, but it triggers my BS detector.
- agentultra 1y agoThis is why I like strong, sound static type systems with interesting features. I’ve had similar experiences working with a large 1m+ SLOC Haskell codebase. It was straightforward to make large refactors because of the type system. And we weren’t even using fancy things like linear types. Just plain old Haskell with a sprinkling of dependent types in core, critical sections.
- akshay-deo 1y agoComparing anything with JS or TS would make it shine. The biggest problem I personally have with Rust is that there are a hundred ways (not literally ) to do one thing, which throws readability out the window, especially when you have developers at different levels of expertise.
- jrpelkonen 1y agoCan you elaborate or maybe give some examples about having too many ways to do one thing? I have never heard this complaint about Rust before. Rust even has tooling (clippy) that warns about unidiomatic code.
- staticelf 1y ago[dead]
- johnfn 1y agoI think Rust is awesome and I agree with that part of the article. What I disagree with is that it's the fault of Typescript that the href assignment bug is not caught. I don't think that has anything to do with Typescript. The bug is that it's counter-intuitive that setting href defers the location switch until later. You could imagine the same bug in Rust if Rust had a `set_href` function that also deferred the work: set_href('/foo'); if (some_condition) { set_href('/bar'); } Of course, Rust would never do this, because this is poor library design: it doesn't make sense to take action in a setter, and it doesn't make sense that assigning to href doesn't immediately navigate you to the next page. Of course, Rust would never have such a dumb library design. Perhaps I'm splitting hairs, but that's not Rust vs TypeScript - it's Rust's standard library vs the Web Platform API. To which I would totally agree that Rust would never do something so silly.
- flohofwoe 1y agoObjection your honor ;) A 'setter' should never ever cause an action to be triggered, and especially not immediately inside the setter. At the least change the naming, like `navigate_to(href)`. But in the browser environment it's also perfectly clear why it is not happening immediately, your entire JS code is essentially just a callback which serves the browser event loop and tells it what to do next. A function which never returns to the caller doesn't fit into the overall picture.
- johnfn 1y agoThat’s a good point. I actually modified my comment because I assumed everyone would take for granted that no work should be done in a setter :) > A function which never returns to the caller doesn't fit into the overall picture. Hmm, not sure about this. On the node side, you can process.exit() out of a callback. If setting href worked like that, I think it would be less confusing.
- mcherm 1y ago> If setting href worked like that, I think it would be less confusing. How do you imagine this would interact with try-finally being used to clean up resources, release locks, close files, and so forth?
- internet_points 1y agoThat same graph applies to Haskell. You could also exchange the x-axis with - number of different projects you work on - time since you last worked on the project - number of different people working on the project without the lines changing much.
- misja111 1y agoCome on, the author is comparing Rust to Typescript, Zig and Python, and then concludes that Rust becomes more productive in larger codebases compared to 'some other language'? Could it be that those 3 languages were not really designed with large (backend) codebases in mind? To do a fair analysis the author should have compared with a language like Java, Scala or C#. About the article itself: the part of wrapping a structure in a mutex because it is accessed concurrently is a bit of a red flag. If you are really working in large codebases, you'd like to avoid having to do that: you'd much rather encapsulate that structure in a service and make sure all access to the structure is queued. Much simpler and less chance for nasty deadlocks.
- Ygg2 1y ago> To do a fair analysis the author should have compared with a language like Java, Scala or C#. Wouldn't that be unfair to them? They still use null or Nullable. They don't have ADTs, their thread-safe invariant is maintained by docs, etc.
- misja111 1y agoMaybe. But at least these languages were meant for large applications so the outcome of the comparison would be more meaningful. Apart from that, Scala uses the Option type instead of null, and it has ADT's; And I dont know about C# but in Java and Scala using low level mutexes is considered a code smell, the standard libraries provide higher level concurrent data structures. On top of that, Scala provides several widely used IO frameworks that make dealing with concurrency and side effect much more simple and less error-prone, it would win the comparison with Rust here.
- Ygg2 1y ago> On top of that, Scala provides several widely used ... With Scala is less what you miss, and more what you have. - Custom operators. Those are great for DSLs and rendering your code to look like utter gibberish, and confuse IDEs. - SBT. Shudder. - Scala 2 codebases.
- amai 1y ago"I have found that Rust's strong safety guarantees give me much more confidence when touching the codebase. With that extra confidence I'm much more willing to refactor even critical parts of the app, which has a very positive effect on my productivity, and long-term maintainability." That is usually why you have tests for your code. But if you have no tests a programming language with a strict compiler is of course more helpful. But the best is to write tests. Then you can also refactor code with confidence written in "sloppy" programming languages.
- lock1 1y agoAgree with your point. Writing good tests & utilizing type system properly should help a lot with catching bugs. But for some reason writing tests always reminds me with xkcd "Standards" (https://xkcd.com/927/ https://xkcd.com/927/). Instead of "fixing standards by create another standard", now it's catching code bug with more code. At least for type system, it gets maintained by language maintainers, not project maintainers.
- IshKebab 1y agoNope, it's better for properties to be statically proven by the compiler where possible, rather than tested with runtime tests. Tests should be used where you can't prove correctness statically. But it's better if you can. The ultimate end point of this is formal verification, where you need very few - if any - runtime tests. But formally verifying software is extremely difficult, so you can't usually do that.
- pjmlp 1y agoEven formal verification requires tests though, because there may be bugs on the logic proofs.
- IshKebab 1y agoTrue. Well usually. In some cases the property you're writing is so simple you wouldn't need any tests, for example in a compression library you just check decompress(compress(x)) == x. If that is proven there's no need for tests. But yeah usually it's a good idea to have a few tests anyway. You just need fewer tests the more properties you can prove formally.
- phplovesong 1y agoThis boils down to "dev found out that a static typesystem is usefull". Its always funny when i see these kinds of posts.
- bkolobara 1y agoMy intention with the blog was to show that Rust's lifetime tracking and trait system allow you to catch much more complicated issues than just "one type doesn't match another" errors. That's the main reason why I chose to compare it with TypeScript, that is also a statically typed language, but just can't catch some issues that Rust can.
- Tryk 1y agoThe notion of a type is not fixed, Rust has a more generous conception of what is a type and what its static type system checks.
- phplovesong 1y agoThe bug you showed had nothing to do with typescript (JS) but the browser API. Typescript is not a good languge, but still a way better option than vanilla JS. I kind of wish Haxe would have taken TypeScripts place as it is simply a better language overall.
- deleted 1y ago[deleted]
- wmanley 1y ago> Its always funny when i see these kinds of posts. What's funny about it?
- phplovesong 1y agoBasically that you list the obvious benefits we knew since the 70s (in a new blogpost). Its a form of re-discovery, and im happy people "find" the joy of it. Rust borrows heavily from the other ml's and thats why many like it, not realizing there is decades of research behind the whys.
- hoppp 1y agoIts great that the difficulty of rust actually helps refactor large codebases.
- TeeMassive 1y agoIn my experience, people tend to be more annoyed by the time spent writing code and overcoming the obstacles that are not "solving the problems" but will simply zen out the hours spent debugging, refactoring and testing manually with a meditation podcast.
- wedesoft 1y agoOr you could use Clojure with the immutable data structures where you don't need to lock a mutex when accessing a data structure concurrently.
- CupricTea 1y agoFearless refactoring is THE reason I rewrote an internal tool at work from Python to Rust. When the code got sufficiently large and a refactor was in order, I suddenly realized how loose everything is semantically tied together. I understood why so many Python developers like writing so many dumb unit tests. They're compensating for the lack of a strong static type system[1]. This is an internal tool that isn't our main product so I don't see any value in spending extra time writing needless long-winded "did the user pass the right thing" tests or getting a static analyzer going so I just chose a language with as little build system/static analyzer/built-in unit testing/separate runtime installation headaches as possible to get things going easier. That was three years ago and the tool is about 4x more featureful than when it started. [1]: https://dmerej.info/blog/post/i-dont-need-types/ https://dmerej.info/blog/post/i-dont-need-types/
- api 1y agoRefactoring is a huge problem for all dynamic languages. It’s so easy to break things in ways that do not appear until runtime. They are very productive for first drafts. Then the code tends to “melt” as more people work on it or you do refactors because there is no static type system to catch obvious problems or enforce any order.
- CupricTea 1y agoI definitely agree with first drafts. To this day I still fire up Python for quick ideas and one-off scripts because it's just so damn convenient. It wasn't until I tried writing larger programs in it where I first felt how brittle it really is.
- _davide_ 1y ago100% agree, in Rust things usually just work out on the first try! Time spent debugging is halved and you can focus on the actual product