kibwen 2 years ago

Having the new borrow checker finally on in all cases is nifty. For valid programs the behavior is the same as the previous version, but in the event of a compiler error, previous versions would still show you the output of the old borrow checker. I've already had one case where someone came asking about a confusing borrow checker error message and I just had to suggest that they try compiling with 1.63 and the new error message was clear enough that it answered their question immediately.

Now we just need to do it all over again once Polonius finally arrives. :P

  • brundolf 2 years ago

    Migrations aren't ideal, but I'm excited to see continued innovation in borrow-checking. This is a major area where Rust is exploring truly new territory as a language, so growing pains are to be expected, and it's encouraging to me that Rust's semantics allow borrow checking to keep getting better without breaking changes

    • cwzwarich 2 years ago

      > it's encouraging to me that Rust's semantics allow borrow checking to keep getting better without breaking changes

      Just to be clear, these are breaking changes. If the new borrow checker didn’t break existing code, they would have removed the old one long ago. They’ve just decided that the observable effects of the breaking change in the ecosystem are small enough (especially after warning about them for years) that they are okay making the breaking change without a semver-incrementing Rust 2.0.

      To be fair, the reason for the breaking changes is generally to fix soundness issues (of course, not every example of code now banned will exhibit unsoundness), but that doesn’t cover all of them. There were some completely sound (albeit useless) examples of partial initialization that broke with NLL:

      https://github.com/rust-lang/rust/issues/21232#issuecomment-...

      https://github.com/rust-lang/rust/issues/54987

      Strict adherence to semver would require still accepting this code.

      • brundolf 2 years ago

        Sure, I guess all I meant was that the fundamental semantics of borrowing and lifetimes that were chosen at the beginning have continued to scale up to a better and better borrow-checker experience. They haven't found deep limitations of the language that prevented innovation without going back to the drawing board.

  • pavon 2 years ago

    I'm probably mixing different things up, but I thought the non-lexical lifetime changes made the borrow checker more permissive in the sense that some code that the compiler would reject using the lexical borrow checker, would be allowed using the non-lexical borrow checker.

    If this is so, how are they maintaining edition compatibility? It seems like you could write code for the 2015 edition that would build with the new compiler, but not an older (2015 edition compliant) compiler.

    • kibwen 2 years ago

      The new borrow checker both allows more to compile (by being more precise about tracking control flow, allowing it to prove more advanced things about lifetime usage) and also less code to compile (again, by being more precise about tracking control flow, allowing bugs in the old borrow checker to be fixed (see https://github.com/rust-lang/rust/issues?q=is%3Aissue+label%... for examples)).

      The edition compatibility story here is interesting. When the 2018 edition came out, the new borrow checker was only turned on for code on the 2018 edition. Meanwhile, in the 2015 edition, it would run both borrow checkers and yield a warning if your code passed the old borrow checker but not the new one. After a migration period, the new borrow checker was eventually made the default on the 2015 edition as well. Because of the aforementioned soundness bugs fixed by the new borrow checker, this was seen as acceptable (soundness fixes are explicitly allowed by the stability policy, although the Rust developers still try to provide migration periods to make them less painful).

    • kam 2 years ago

      Editions only guarantee that old code builds with new compilers, not vice versa. New features are added to all editions unless they require e.g. new keywords that are available only in new editions.

      • pavon 2 years ago

        Ah, that is right. I apologize, I think I've learned that three times now, but for some reason my brain refuses to accept it.

        I like that previous editions get new features when possible, unlike say C++, but I wish they'd gone with semantic versioning for the language version, like python did when both 2 and 3 were active. Oh well, I just need to remember that the edition is like the major version, and compiler version is sort of like minor version, and both are needed to specify forwards compatibility.

        • brundolf 2 years ago

          I'm not sure semver would really do it here, because then you could have say:

            2.17
            1.17
          
          What would you call the compiler binary that could compile both language v1 and language v2, with (shared) minor-version .17?

          The issue is that semantic versions are a hierarchy, whereas Rust edition x compiler version are a matrix

        • game-of-throws 2 years ago

          It might make more sense to think of editions as optional changes to the surface syntax, and nothing more. They are not nearly as important as the name makes them sound.

          • brundolf 2 years ago

            They are often small, but it's a mistake to say they're surface-level. Changes to syntax or semantics only get made when absolutely necessary to allow for essential features or fixes.

    • game-of-throws 2 years ago

      Editions are orthogonal to compiler versions. It is perfectly fine if code builds with a new compiler but not an old one. Another example of this: you can write code in a 2015 edition crate that calls the new `std::thread::scope` function.

    • Macha 2 years ago

      Editions promise old code works with new compilers.

      They do not promise new code works with old compilers

GolDDranks 2 years ago

Scoped threads are something that really showcases Rust's features around thread-safety, lifetimes, sharing and mutability. I'm glad it's finally back in the standard library!

  • davidatbu 2 years ago

    I would be very interested in hearing from experienced folks about how important this is/what useful patterns it unlocks.

    I ask because I remember when trying to learn Kotlin, the docs made a big deal of structured concurrency[0], and golang of course is known for its concurrency story.

    [0] https://kotlinlang.org/docs/coroutines-basics.html#structure...

    • klabb3 2 years ago

      Sadly, this is only lexical scoping which means you can only borrow from the outer scope and not ad-hoc the normal way borrowing works in Rust.

      In short, this means that thread pools and async runtimes cannot use this feature to provide borrowing across tasks. We're still stuck with Arc-ing everything that crosses task boundaries.

      Prior to 1.0, there were "true" scoped threads (with a lifetime 'a on the join handle), which let you borrow from the parent thread. (This could have extended to tasks as well, had they been around.) unfortunately it wasn't safe together with the much more popular Arc/Rc, which wasn't 'static restricted, despite it's ability to cause cycles and leak. So those powerful scoped threads were removed and "destructors are not guaranteed to run in safe Rust" was formalized instead.

      Seeing how popular async became, I wonder if this was the only and right way to move forward. I'm not against Arcs, they have their place. But if you can use normal RAII it's much preferable.

      • GolDDranks 2 years ago

        How is this different from the prior 1.0 scoped threads? I remember the whole "Leakpocalypse" event that lead to the deletion of the API, (and subsequently, to invention of this API, where the scope guard is represented as an additional closure) but those were entirely lexical too, right? Rust borrows in general are pretty much lexical in the sense that you only analyze the body local function, nothing more. Rustc can't reason about dynamic invariants, you need runtime checks for that.

        • masklinn 2 years ago

          Probably some sort of flexibility / infectivity e.g. let’s say you have a function a which calls a function b with some borrowed data, b wants to spawn and return a thread (possibly wrapped in some other structure).

          In the original acception I think you could literally do that, a scoped thread would just be a normal RAII object with various lifetime bounds.

          With the new scoped threads, I’d think you need to create the scope in a and pass the scope handle to b so it can spawn the thread correctly.

      • oconnor663 2 years ago

        > async runtimes cannot use this feature to provide borrowing across tasks

        They can't use this function, but with some duplicated unsafe code they can still provide similar APIs for their callers, right? Things like this: https://github.com/jaboatman/tokio-scoped

        • klabb3 2 years ago

          I just checked quickly so may be wrong, but it looks like they're pulling the same trick as with the new scoped threads, ie only lexical scoping. Note also that the scope function is blocking and called from within an async fn, which is normally not great for composability with other async code.

          That said, taking a step back it has crossed my mind that bringing back true scoping with unsafe in the impl might be an acceptable way forward, despite how upsetting this is to a lot of rustaceans. I think that the failure modes (ie triggering UB) for this case can be simply enumerated in a short list of "DON'Ts" (such as putting a scoped join handle in an Arc).

          OTOH, since leaking was formalized and explicitly allowed (heck, even borderline encouraged through mem::forget), god knows if people have exploited that for other cases in true Hyrum's law fashion, that breaks the abovementioned idea. They certainly had the right to.

    • masklinn 2 years ago

      > I would be very interested in hearing from experienced folks about how important this is/what useful patterns it unlocks.

      For Rust it allows thread to play better with lifetimes, which means there are scenarios where you don’t need the overhead of a lock (and usually the Arc that does with it, unless you go with a global) or queue, you can just work with on-stack “unsynchronised” borrows and it’s thread safe.

      That means lower syntactic and runtime overhead in the cases where it’s an option.

      It was already available in crossbeam so it’s not novel novel, but not needing a dependency for it is useful still.

      • davidatbu 2 years ago

        Thanks! I didn't know that it was already available in crossbeam, that helps a lot in understanding the significance of this feature.

jmartin2683 2 years ago

Rust is the greatest. It’s my go-to for virtually anything that it could reasonably be used for (a lot). After using it exclusively for a while and getting comfortable and fast with it, I can’t see any good reason to use anything else for most purposes.

  • felipellrocha 2 years ago

    I am on the same boat. It's just too good. There are flaws, for sure, but it seems like the language itself is going on an incredibly good direction.

  • pjmlp 2 years ago

    GUI programming at the same level as MAUI, SwiftUI, Jetpack Compose, Qt, WPF,...

    • sebzim4500 2 years ago

      Yeah GUI is really the biggest missing part of the ecosystem. To be fair it is an extremely difficult problem; there is a reason there are so many electron apps out there.

      • pjmlp 2 years ago

        Not really, the reason is laziness and seeing nails everywhere, instead of picking the right tool.

        Coding GUI/TUIs since MS-DOS and Amiga 500 days, and Web since 1996.

        • pas 2 years ago

          A lot of times a cross platform non-native GUI is the right tool. Same UX on every platform. What's not to like? Allows faster iteration for small teams, you can use TS and doesn't even have to think about Swift/ObjC/Java/Kotlin/C++, and you can use whatever you like for the backend.

          • int_19h 2 years ago

            > Same UX on every platform. What's not to like?

            Speaking as a user, that's exactly what I don't like. I don't care about same UX on every platform; I'd rather have consistent UX across apps on the same platform.

            • pas 2 years ago

              Sometimes you work on a platform that does not have a native style. (Eg. kiosks.) Or the native app is just an extension of a very website-driven service. (Eg. an app for event management, ticket admissions.)

              As a user I prefer something that works, making sure one thing works consistently is easier than testing different native things that implement the same functionality. (Oh, and as a user I very much prefer that it does implement the same functionality.)

          • FridgeSeal 2 years ago

            > What's not to like?

            Native interfaces and behaviour are preferable over whatever options the app developer chooses to support.

            In the case of electron apps, effectively running multiple browsers is also in the “what not to like” basket.

            Native apps often have less input latency, and are significantly nicer to use.

            > Allows faster iteration for small teams

            As a user, I’d much, much prefer slower iteration if it meant the resulting app was better. Electron apps thrashing out pointless updates every other day just because they can isn’t always a positive in my book.

            • pas 2 years ago

              What's the native interface when Apple/Google/Samsung changes that every year (or two years)? What's the native interface of a kiosk at a car wash? (Where we used a touch screen running full screen Firefox and the users saw the same thing as they saw on their phone where they managed their wash coupons and whatevers.)

              On desktop I don't run slack, discord, teams (and whatever wants to run separately) separately, if something that doesn't do meaningful local I/O and/or computation cannot run in a browser, then I don't use it if I can avoid it at all. And I hopefully can keep my streak of not developing nor shipping any forced electron based mess.

              VSCode is fast. I'm surprised too. While native Windows things are just slow all over (with a beefy modern desktop).

              Seriously, how the fuck can the start menu be this ridiculously slow? I have like 50 things installed, and I use about 5 of those. And it takes forever to find those as I start typing. (I have KDE flashbacks!)

              Oh and super native Firefox is also a disaster when it comes to speed (I search for the same bookmarked pages in the awesomebar - the combined location and search bar, I have disabled the search part, because the UX was horrible, so it should just search in the local recency shorted LRU cached list of pages ... and it's dog slow and dumb).

              Big billion dollar unicorns push those meaningless updates, not small teams (at least in my experience).

          • shakow 2 years ago

            > Same UX on every platform. What's not to like?

            Everything. Make a website if that's what you want.

            • pas 2 years ago

              Yes, I want to do that, but Apple, Microsoft, and Google (as Android) continues to veto that. And there are some arguments for why (the advantages of the walled gardens of the "curated" platform stores, the tech inertia of the different APIs of apps vs websites, and the learned user behavior).

              Mozilla tried "Firefox OS" and it's hard to do it. Chrome tried service workers and persistent sites/apps, but.. it's just lame compared to shipping a separate app that bundles a web view and a backend.

    • debug-desperado 2 years ago

      Probably a lost cause at this point since most GUI toolsets are too object-oriented to comfortably wrap and call with Rust. Do a web view with Tauri instead.

      • pkolaczk 2 years ago

        I haven't found any problems using GTK bindings in Rust. It is actually easier and nicer than in C, especially with additional support like relm4 (but even the manual approach is good enough). Who said GUI must be OOP?

        • pjmlp 2 years ago

          The Gtk+ library you depend on.

      • lenkite 2 years ago

        Hoping Tauri supports mobile at some point. Maybe it needs to wait till the EU bureaucrats force open Apple's walled garden.

      • pjmlp 2 years ago

        Or chose a better tool for the job, picking languages based on platforms, not picking an hammer and then trying to find a nail that fits.

        • pornel 2 years ago

          GUIs don't exist by themselves, but serve a program. Rust may still be the best language for the non-GUI part of the program.

          I've once picked ObjC for a whole program based on its native GUI, and spent way too much time chasing bugs caused by its thread-unsafety (Cocoa bindings are a trap in multi-threaded programs).

    • jmartin2683 2 years ago

      I will say thats one thing that I do absolutely none of. A lot of movement in that space, but clearly a lack of support on the level of C/C++.

  • monkmartinez 2 years ago

    I have some serious FOMO with Rust... I have been using JS and Python for everything the last few years. Will you please recommend some Primers/Resources/Tutorials for Rust for someone like me? That is, basically a script basher that glues everything together with Python.

    • dwaltrip 2 years ago

      JS and Python are my primary languages. I've toyed around with Rust, and it is pretty cool. There were a number of nice moments where I was surprised with how much it felt like I was writing high-level code. However, the overall learning curve is huge, especially if one is mostly used to garbage-collection (like me).

      If you are doing web stuff, I'm sure Rust can be used effectively. But it is non-trivial and there are serious tradeoffs (learning curve, lack of ecosystem for web, hiring, etc). Teams would be better off sticking with python or JS for most web projects. Some perf-heavy backend or infra projects could possibly benefit from Rust, but otherwise it doesn't seem like the best choice to me.

      It is definitely a cool language that seems to be gaining serious momentum. Could be fun to mess around with.

    • jmartin2683 2 years ago

      I just read the book (the Rust programming language, free online) cover to cover and started porting all of our Python (command line) apps. It was much, much easier than the internet led me to believe going into it. Just take the time to really internalize them ownership stuff… the rest is syntax and patterns.

  • realharo 2 years ago

    The compilation speed is not the greatest.

    Other than that, it's a great language with a great ecosystem.

  • nesarkvechnep 2 years ago

    I like Rust a lot but I don’t see a reason to use it for software systems instead of Elixir.

  • Tainnor 2 years ago

    Rust is probably great for systems programming, and might have other nice, modern features that make it compelling outside of that niche, but as a web developer, I simply don't care about manual memory management and fighting with the borrow checker.

    I understand why it exists for certain tasks, but I can just use a garbage collected language instead (preferably a modern and expressive one, like Kotlin) and save myself a lot of trouble, because in the end, the performance hit often doesn't matter.

    • davidatbu 2 years ago

      You know, I would be with you, but:

      1. The versatility of Rust means I can use it for truly "the full stack". E.g., in my current side project, I'm using rust to do audio decoding, resampling, running a speech recognition model, the backend of the app, and the frontend! Though I haven't yet started on all of those components, I expect to get some major simplification in my code because of this.

      2. The "modern and expressive" languages all have fatal flaws, _for me_. Swift has a poor cross platform story/community, Kotlins tooling is poor if you don't use intelliJ, ocaml has a reputation of poor documentation. I understand that not all of these things will bother people as much as they bother me, but they do bother me a lot. Tbh, I'm still on the lookout for a lang that is actually statically typed(no TS or python), has an expressive type system, a good cross platform story, good, non proprietary tooling, and is at a slightly higher abstraction level than Rust.

      • Tainnor 2 years ago

        1. sounds like a project where Rust is indeed a good fit.

        As for 2, I'm very happy with Kotlin for the most part. I agree that there's no point in trying to use it without IntelliJ, but... IntelliJ is really good, and the community edition is free. Yes, the startup times can be slow and annoying, but what I get in return is IMHO worth it (even more so if you use Ultimate and frameworks like Spring). Also, command line tools for Kotlin are IMHO better than for Java, at least (e.g. ktlint). I agree that Swift outside of Apple software is a dead end.

        Given your constraints, though, why are you ruling out TS (I've never used it, but I heard good things)? And what about Haskell?

        Though, in all fairness, in your case you're probably comfortable enough with Rust's borrow checker that it doesn't slow you down that much anymore, so I understand why you'd continue using it. For someone who has never worked with it, it's a different story.

        • davidatbu 2 years ago

          > Why not IntelliJ?

          My (admittedly poor) excuse is that I really wanna keep using neovim (I really like the customizability) :D

          > Why not TS/Haskell?

          TS is amazingly expressive, and probably my choice after Rust for traditional full stack! But it's sometimes the case that the underlying dynamic nature of JS "leaks through" (especially when using third party libraries), and it does come with the rest of the JS baggage (undefined == null, no easy pattern matching, ..etc).

          Haskell? I just realized that, from reading the interwebs, I've had an unconscious bias against Haskell as a mostly academic language unfit for industry use cases except in very narrow niches, but I def should check it out and make up my own mind.

          > Though, in all fairness, in your case you're probably comfortable enough with Rust's borrow checker that it doesn't slow you down that much anymore.

          True. On the rare occasion that it does, I just use the "cop out" of doing `.clone()`. I hope to get better though!

          • Tainnor 2 years ago

            > My (admittedly poor) excuse is that I really wanna keep using neovim (I really like the customizability) :D

            I used to be a big "vim for everything" person. Now, less so. I just use vim mode in my editor/IDE of choice, because what I'm really after is the superior text editing. Now, depending on the project and language, I'll use vim, vs code or IntelliJ. But I can see that if you're really into all the vim customisability, it's different...

            • davidatbu 2 years ago

              Just wanna say thanks! This was a really educational thread for me!

    • jmartin2683 2 years ago

      Building a basic rest api with rocket is no more difficult than doing the same with Sinatra or flask once you have the hang of it. Arguably much easier if you count time chasing down bugs from unexpected runtime behavior.

ydnaclementine 2 years ago

What is everyone building in rust, and how do the killer rust features (mentioned in other comments) help you?

I'm not anti rust in any way, it's more of having limited time and so many languages problem. But I'm interested in the types of problems rust is good for, so I could consider it if/when I encounter them

  • ekidd 2 years ago

    I've built a fair amount of production software in Rust. The best features are:

    - Rust is naturally pretty fast, on the rough order of C++ in many cases. Even unoptimized programs tend to be very snappy (as long as you remember to compile in release mode and buffer I/O).

    - Rust tends to warn me if I make a dumb mistake, rather than having my program mysteriously corrupt memory at run-time.

    - Rust has very nice support for portable CLI applications, both in the standard library and in third-party libraries.

    - With a bit of extra work, I can usually deliver a single, statically-linked Linux binary. Go is even better at this, but Rust does it well.

    - It turns out the "algebraic data types", what Rust calls "enum" and "match", are just a really nice way to write down every possible "case" or "state" of some value. If a piece of software involves hundreds of special cases, Rust allows me to think about them clearly.

    - Rust has surprisingly good third-party libraries for many things. (Not everything.)

    - Multithreaded Rust apps are a dream.

    Cons:

    - Rust forces me to keep track of whether things live on the heap or the stack, whether I'm passing them by value or reference, and so on. This is good when I want performance, but for other kinds of code, it's just a "cognitive tax."

    - Rust's learning curve is higher than Go, but arguably lower than C++. This is especially true if you've either never worked with stack/heap/pointers before.

    - Rust tends to favor "mostly functional" architectures over "mutable objet soup" architectures. This pushes you to use less-familiar designs for games and GUI libraries, for example.

    • cassepipe 2 years ago

      > Rust's learning curve is higher than Go, but arguably lower than C++. This is especially true if you've either never worked with stack/heap/pointers before.

      I tried to learn Rust when I only knew Python and had not CS education, I gave up rather quickly. Then I did some projects in C and later on C++, and now I understand Rust. Because I understand what are the problems it tries to solve. Imho only after you have some skills in C++, and/or some kind of CS education, can you really appreciate what Rust is about.

    • wiseowise 2 years ago

      > Rust tends to favor "mostly functional" architectures over "mutable objet soup" architectures. This pushes you to use less-familiar designs for games and GUI libraries, for example.

      How is that a con?

      • ikawe 2 years ago

        One example would be the lack of pragmatic rust native GUI libraries. There are wrappers for GTK and Qt, which are probably your best option.

        The most popular pure rust ones are based on somewhat esoteric patterns and seem prone to being abandoned.

        Within that there are some interesting options, like for functionally reactive programming and immediate mode guis, but these paradigms aren’t all encompassing and in fact cover a pretty minority use case based on what GUIs are being created today.

        Maybe one day we’ll all only ever create purely functional GUIs and speak Esperanto, but until then, we’re a little hamstrung in rust.

        • game-of-throws 2 years ago

          I think that's more a function of the newness of the language than anything else. Golang is (I say this as a Rust developer) more popular, has been around longer, yet still has the same dearth of GUI libs.

          • int_19h 2 years ago

            I can't speak for Go, but idiomatic Rust is not friendly towards the kinds of object graphs that are typical in traditional GUI frameworks with event handlers etc. You either end up with ARC everywhere, which is a pain because it's not transparent in Rust; or you have to come up with the aforementioned "exotic patterns", which significantly raises the bar for adoption.

            • pkolaczk 2 years ago

              Yet, web developers have been using some of those exotic patterns for ages and they don't complain. They even prefer that way to traditional GUIs by using Electron massively now. Rust GUI frameworks are typically based on strict separation between a model and a view and communication between them with messages which is something much closer to web programming than traditional GUI programming. An OOP soup with arbitrary handlers messing around with state directly in random places is IMHO a bad idea in any language anyway.

              • int_19h 2 years ago

                MVC originated in desktop GUIs, originally for Smalltalk. Java Swing was, I believe, the first mainstream GUI framework which used it thoroughly. Most GUI frameworks since then are also MVC (or some derivative thereof, like MVVM in .NET). This is just a way of organizing code and has little to do with presence of absence of event handlers, or the web.

                • pkolaczk 2 years ago

                  I didn't mean MVC so much, as message passing between the view and the model. Safe Rust has no problem with MVC nor its derivatives.

      • oconnor663 2 years ago

        It's a big piece of the learning curve, and one that usually only shows up when you try to write larger programs. Plus there's very little the compiler can do to point you in the right direction when your object soup needs major refactoring. From what I've seen, a lot of folks either give up on Rust or start writing blatantly unsound unsafe code when they hit this.

        On the upside, a lot of C++ game programming uses an ECS-style architecture, which does work well in Rust.

    • iopq 2 years ago

      When I tried to be more functional, it was really awkward in Rust

      I wanted to write some helper functions that applied an argument to a closure and I got this code

          fn apply<A, B, C, G>(mut f: impl FnMut(B) -> G, a: A) -> impl FnMut(&B) -> C
          // must still be `for<'r> impl FnMut(&'r B) -> C`, because that’s what filter requires
          where
          G: FnMut(A) -> C,
          B: Copy, // for dereferencing
          A: Clone,
          {
              move |b| f(*b)(a.clone()) // this must do any bridging necessary to satisfy the requirements
          }
      
      needless to say, Rust doesn't do automatic currying or any advanced functional language tricks so it's just painful to write the same type of code
      • ekidd 2 years ago

        Yeah, your functional programming skills will probably be most useful at the achitectural level in Rust. Because mutation is very local in Rust, your individual functions can often get away with being imperative, but your overall architecture can't really rely on unrestrained mutation.

        But if we zoom in from the architecture to the individual lines of code, Rust is more of a mix of functional and imperative styles. Rust's iterators can be fairly pleasant for working with lazy streams. Closures are more of a mixed bag. This is partly because closures need to worry about ownership, which complicates things by splitting closures up into Fn, FnOnce and FnMut. And each closure is a separate anonymous type. So, yeah, Rust supports quite a few useful things with closures. But as your example shows, the type declarations can quickly become intolerable.

        So my strategy is to go with the flow, and keep things simple. I only bring out the heavy type declarations on special occasions, when they provide a big payoff. Otherwise I keep things as simple and boring and concrete as I can.

        There are a few popular Rust libraries which make very heavy use of generics. I find that this sometimes backfires, making those libraries slower to compile and harder for me to understand.

    • albrewer 2 years ago

      > I've built a fair amount of production software in Rust.

      Any examples?

  • CJefferson 2 years ago

    Personally, I use Rust as a "better C++".

    Ways in which it is better:

    * A decent package manager

    * "Safe by default" (you have to use 'unsafe' to turn off the safety features), unlike C++ which is "unsafe by default" (v[i] is undefined behaviour if i is out of bounds, for all of C arrays, std::arrays and std::vectors).

    * Really easy to do multithreading -- I had reached the point with C and C++ where I was of the opinion it is "almost impossible" to write large thread-safe programs, and all communication should be between processes with pipes, wherever reasonable. In Rust I again feel I can safely and productively write multi-threaded code.

    The things Rust doesn't let you do (throw pointers around everywhere) can get a little annoying, but I find the extra safety increases my productivity enough that it's worth the tradeoff.

    • dralley 2 years ago

      >(you have to use 'unsafe' to turn off the safety features)

      "unsafe" doesn't turn off any safety features, it just allows you to do additional things which the base language does not, like:

      * use raw pointers

      * use mutable static globals

      * access fields of unions

      https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#unsa...

      All the rules of safe Rust still apply inside of unsafe blocks. The borrow checker still works, etc.

    • tialaramex 2 years ago

      You didn't say otherwise, but this is such a frequent misunderstanding from people who don't write Rust, or write only safe Rust (which is fine) that it's worth pointing out the unsafe keyword and Rust's choice to be "safe by default" are not directly related. Culturally obviously they come from the same place, but for example:

      v[i] is bounds checked in Rust even in unsafe. The unsafe "super powers" don't include "magically now indexing has no bounds checks" but because Rust's get function v.get_unchecked(i) is marked unsafe‡ you would need to mark code unsafe to use that function and that function doesn't have bounds checks.

      If you write:

        unsafe {
           println!("{}", v[i]);
        }
      
      The compiler will point out that unsafe isn't doing anything for you here, v[i] is safe, marking it unsafe is just a waste of everybody's time.

      C++ doesn't have bounds checked array access for its abandoned built-in arrays, but it does (these days) have bounds checked access for std::array and std::vector as a separate member function, however because they aren't the default few C++ programmers use them even where they undoubtedly should. This is an ergonomics issue, it's just easier to write needlessly unsafe C++.

      ‡ This said just get() before I edited it, but Steve corrected me, see below.

      • int_19h 2 years ago

        C++ has had std::vector::at() - which checks for out-of-bounds indices - since C++98. The reason why it's not used all that much in practice is because indexing is relatively rare - most of the time, you just iterate over collections, or otherwise use iterators rather than raw indices. And there's no ability to opt into bounds checking for C++ iterators in the standard - implementations usually provide some kind of checked iterators for debug builds, but performance is such that you'd never want that enabled for release.

        • tialaramex 2 years ago

          > indexing is relatively rare

          This is also true in Rust for the same reason, arguably more true since Rust's built-in array type knows how big it is - but the observation in both languages programmers who want indexing write v[i] because that's the natural way to express what you meant, what's different isn't the frequency of need, only the language defaults.

          > performance is such that you'd never want that enabled for release.

          Let's make sure it's correct before we start worrying about performance

          https://twitter.com/magdraws/status/1551612747569299458

        • pjmlp 2 years ago

          I always keep them enabled for release, performance is good enough.

          Most devs should learn to use profilers.

          • int_19h 2 years ago

            In what implementation, and what guarantees does its debug iterators provide?

            • pjmlp 2 years ago

              The one from your employer, Visual C++.

              • int_19h 2 years ago

                If you mean _ITERATOR_DEBUG_LEVEL=1, they're about 4x slower for iteration. I suppose "good enough" is subjective, but if this much is acceptable, I'd rather use C# - it's in the same ballpark.

      • steveklabnik 2 years ago

        Teeeny tiny note

        > because Rust's get function v.get(i) is marked unsafe

        You mean get_unchecked. get is safe, and returns an Option, so that None can be returned if something is out of bounds.

        • tialaramex 2 years ago

          Doh! I even had the "get_unchecked()" function in front of me on the screen and wrote get() anyway. Thanks Steve.

          • steveklabnik 2 years ago

            No problem. Thanks for explaining what unsafe does and doesn't do; you're 100% right that this is a common misconception that comes up often.

      • pjmlp 2 years ago

        Kind of true, except operator[]() does indeed do bounds checking across all major compilers when compiling in debug mode, and all of them have switches to keep the same behavior in release mode as well.

      • CJefferson 2 years ago

        You are right, I did know this but it's worth clarifying. Thanks.

  • kibwen 2 years ago

    I work on a project that's basically a mini-OS designed to host WebAssembly applications.

    The OS parts need to run on bare metal and do all sorts of cursed shenanigans, where Rust's separation of safe and unsafe code lets us better focus on proving to ourselves that the unsafe bits are working as intended (we also test our unsafe code under Rust's Miri interpreter/sanitizer for extra confidence). IOW, the killer feature is low-level control plus a reasonable expectation of safety.

    The actual WebAssembly interpreter is also written in Rust, which is a good fit because inefficiency in an interpreter leads to inefficiency in anything running in the interpreter. The killer feature here is performance plus safety.

    The overall package is bundled up into a CLI app that lets you deploy an instance of our OS with your application running within. Rust has a fairly nice wealth of libraries that make this pleasant, from CLI argument parsers to cryptography to OIDC and beyond. And it all gets packaged up into a single binary for easy distribution (even the OS and interpreter parts, which get embedded directly in the binary via `include_bytes!`). The killer feature here is great platform support (including cross-compilation) and small binaries (though you may reasonably disagree on "small"; altogether the binary (again, including the OS and interpreter) comes out to 25 MB).

    Finally, Rust's lack of a pervasive runtime and great WebAssembly tooling means that it's easy to compile Rust applications to run on top of all this as well.

    • cultureulterior 2 years ago

      This OS seems interesting! Link?

      • kibwen 2 years ago

        Keep in mind that I'm using "OS" here loosely; it's a bare-metal program that exposes a subset of Linux syscalls so that the interpreter (which itself is compiled for Linux) can run in the extremely specific context that we target. It's still early days so I won't spend too much time shilling it, but all the code is open source and lives under the umbrella of the Linux Foundation: https://enarx.dev/

  • Macha 2 years ago

    Rust has replaced Python for me in my personal CLI tools where I expect to use them as an ongoing basis. At this stage I am almost as fast writing Rust as Python, find the Rust projects much easier in terms of environment setup than the whole pipenv/poetry/pyenv world, and enjoy the instant response of not having to wait for a python interpreter startup.

    I also have some side projects like a gameboy emulator, where it's nice to have what is effectively a lower level language with the safety and features of higher level language.

    My day job is mostly Java/Javascript though, rewriting the world isn't yet worth it, but there's certainly one or two of our systems where if a rewrite in another language was on the table I'd make the case for Rust

    • Scarbutt 2 years ago

      At this stage I am almost as fast writing Rust as Python

      By generously use of copying?

      • oconnor663 2 years ago

        The "One Simple Trick" that lets you port most Python code straight to Rust is to take all the aliasing "object soup" references that Rust doesn't like, shove those objects in a Vec or a HashMap, and use indexes/keys instead of Rust references. This does require plumbing the Vec/HashMap around through different functions, but if you do it this way from the beginning it's not much extra work.

        • int_19h 2 years ago

          At that point, what's the benefit of using Rust over basically any other high-level language that has ADTs and pattern matching? Since you lose all the benefits of lifetime tracking that are unique to Rust.

          • mawfig 2 years ago

            What high-level language with ADTs and pattern matching are you thinking of?

            If OCaml, Rust doesn't have weirdly sized number types and has a decent library ecosystem.

            If Haskell, you don't need to understand monads to write hello world.

            If Scala, you don't need to deploy a JVM with your app.

            If F#, you have traits which are super useful and you aren't fighting against MS's refusal to invest in the nicest language in the .Net ecosystem.

            Even without "lifetime tracking" and liberal use of Clone, you can still outperform most programs written in any of the above languages.

          • oconnor663 2 years ago

            > you lose all the benefits of lifetime tracking that are unique to Rust

            I think I understand what you're referring to, but stop me if I get this wrong. If you put a bunch of objects in a Vec and refer to them by index, you have to be careful with operations like .insert() and .remove() that shift Vec elements around and change their indexes. Also if you make each element an Option<T> to support deletion without .remove(), you still have to be careful about indexes of deleted objects, because you might put a new object in the same spot later. I have several thoughts about this:

            - If "memory safety without garbage collection" is one of the unique features of Rust, you still have that. A reused Vec index in safe Rust code will never corrupt memory or trigger a segfault or anything like that. It's strictly a "logic bug".

            - These logic bugs only apply specifically to the objects you put in a Vec and track by index. Creating a Vec<Foo> and getting an index mixed up doesn't change anything about Rust handles your local variable of type Bar (or for that matter, your local variable of type Foo).

            - These bugs are understandable for beginners, and you can follow what's going on with print statements. Contrast that with a dangling pointer into a C++ std::vector that just reallocated, where printing the freed object often appears to show good data, and explaining the bug means talking about malloc/new and the heap. Relatedly, unwrapping a None in Rust will always panic and will never coincidentally appear to work. (Your comment was comparing Rust to higher level languages, but I've heard similar comparisons to C++.)

            - There are good options for fixing the problem. Beginners might prefer to put their objects in a HashMap with an incrementing key. There's a performance cost to that, but it's familiar and relatively footgun-free. More advanced folks might reach for something like "generational indexes into a slab", which gives you back some performance in exchange for making you learn a bunch of new buzzwords and figure out which implementation to choose from crates.io.

            So yes, with those caveats, putting objects into a Vec does turn some compile-time bugs into runtime bugs. That is a downside, much like Rc/Arc/RefCell/Mutex come with runtime downsides. But I've heard folks describe it in terms like "turning off the borrow checker", and I don't think that's a very accurate way of describing it.

            • int_19h 2 years ago

              As you say, I was specifically comparing to high-level memory-safe languages, not C++. You can't get a dangling reference in Java or C#. Not only that, but you can't get into a situation where some reference points to one object at some point of time, and to another object at a different time - but you can with indices.

              The reason why I describe it as "turning off the borrow checker" is because that's exactly what it is - those indices are pointers semantically, but there's no ownership tracking for them. So for them, you turned off the checker. The more you use them, the less checked your code is. If you use this approach in some isolated piece of code, it's one thing. But if it's the go-to solution, then it's reasonable to wonder why you'd do that instead of using a language that has built-in ergonomic safe references with no borrow checking.

              • oconnor663 2 years ago

                > but you can with indices

                Regular Vec indices yes, but not incrementing HashMap keys or other fancier things, if we set aside overflow issues.

                > But if it's the go-to solution

                Oh yeah, I should clarify this point. Rust definitely prefers to use simple ownership (i.e. the ownership/reference graph is a tree) wherever possible. As an example, say we've got a Python program with Person objects and Dog objects. Each Person has a `pets` collection that might contain some dogs, and each Dog has an `owner` field that points back to their Person. When we port this program from Python to Rust, Rust isn't going to be happy with the circular relationships between these types, and trying to implement `pets` or `owner` with references probably won't compile.

                In cases like this, the most ideal, most idiomatic, go-to option is to break the cycle and try to achieve simple ownership. In this case, that would probably mean making the `pets` collection hold Dogs by value, and removing the `owner` field entirely. Any Dog methods that previously referenced `owner` would need a short-lived reference passed in as an extra argument now, or maybe we could change some of them into Person methods. If we can express our program in this style, that's almost certainly what we want to do.

                But there are lots of programs where this doesn't work, at least not everywhere. Maybe a Dog can have multiple owners. Maybe a Dog can have no owner at all. Maybe Dogs want to track their relationships with other Dogs. If people and dogs are independent entities walking around in a game world, or if they represent rows from a couple of tables in some relational database, we probably have lots of problems like this. This is where we start reaching for patterns like Rc<RefCell>/Arc<Mutex> or indexes pointing into Vecs and HashMaps. (I think it's interesting that those Vecs and HashMaps look a lot like db tables.)

                A point I want to emphasize here, though, is that even when the most important relationships in our program use these patterns, the majority of our object relationships are probably still simple. If each Person has a `name`, that's still an owned string. If they have an `age`, that's still a regular integer. When our program reads config values from the filesystem, all of our file handles and protocol data still follow simple ownership rules, use destructors for cleanup, and definitely don't get aliased anywhere.

                In contrast, if we port our program to Java (or keep it in Python), we can't statically guarantee any simple ownership. Any time we pass a Person or a Dog or a HashMap or a byte buffer to some function, we might worry about that function retaining a reference to it, and we might start making defensive copies or reaching for immutable types. We can still use lots of simple ownership, and for most of our objects we probably do, but we've lost the benefit of a compiler that can check that for us.

                Kind of a tangent: When we make aliasing mistakes -- which we can do in Rust in these fancy arrangements, or in Java/Python whenever our data is mutable -- that usually leads to "spooky action at a distance" bugs. Some operation on `foo` has mysterious side effects on `bar`, etc. We've all been there. But I think where these bugs graduate from "annoying" to "insanity-inducing", is when multiple threads get involved. That's when we really want the option of simple, statically-checked ownership, and that's where I think Rust-isms like "Mutex owns the data it protects" are a big improvement over the tools we had before.

            • kaba0 2 years ago

              Logic bugs of this sort are way worse (arguably) than some memory corruption errors you might see in C. The latter can be caught by a very manual usage of valgrind and the litany of similar too, while in the former’s case, you have to find that you even have an error, and track it down.

              Sure, a beginner may not know about these tools, but my point is about the manuality if fixing the problems, one almost doesn’t need any thinking .

              • oconnor663 2 years ago

                I do agree with that point as far as it goes. There are definitely cases (like single-player games) where logic bugs like these suck up more developer time than memory corruption bugs. But I'd push back in a couple ways:

                - Much of the time, maybe even most of the time, memory corruption means security vulnerabilities. Logic bugs can be security bugs too, of course, but at least we can reason about them in local terms like "Is this particular part of the program security-sensitive?" Memory corruption doesn't admit the same kind of local reasoning. For any program that touches input from the internet, that's a huge deal.

                - Just like C programmers can reach for Valgrind, Rust programmers can reach for design patterns that are more robust than Vec<T>. In this case, HashMap<u64, T> with an incrementing key is a very robust pattern. In practice (until you overflow that u64), that pattern catches 100% of your use-after-free-style mistakes. And I think a major advantage of Rust's approach here compared to Valgrind/ASan, is that it runs in production, so it works even if your test coverage isn't amazing.

          • ithrow 2 years ago

            You get to be a member of the rust evangelism strikeforce.

            Joking aside, I guess devs are lured by the tooling, performance, single binary output and enthusiastic community (more people can see your projects).

  • __david__ 2 years ago

    For me the killer feature of Rust is its nice broad scope. I've done:

    - embedded programs on avr hardware, which is impressive that something like rust can compile down to an 8 bit micro. Doing some sort of crazy map/iterator filter closures and finding the resulting assembly being nice and tight makes me super happy.

    - web servers and clients (a scraper that collects scores from a web page and serves its own page of aggregated scores to my family).

    - a low level opengl tmux/terminal client (Rust's Enums were a killer feature here)

    - a windows GUI program for installing a PC game mod (99% developed on a mac and cross-compiled)

    - a cli app to "compile" svgs down to png sprites for a web based game

    - a launcher for Emacs that uses native cocoa apis to display a dialog on error

    Each of these cases ended up pushing different parts of Rust for me. I would say the killer feature that spans all of those is Cargo and the crates.io registry. There are a ton of very good libraries out there—it feels like the heyday of CPAN or RubyGems to me.

    I also quite enjoy how many programming errors Rust finds at compile time. With Rust code I spend way more time fixing compiler errors, but when it compiles I typically don't find a ton of major logic errors. I personally find it extremely rewarding when I get something to compile and then it just works. This happens more in Rust than in other languages I've heavily used.

  • dymk 2 years ago

    I've written no code professionally in Rust, but a fair amount of personal project code. I've written C++ both professionally and as a hobby. At least for hobby use, comparing the two, Rust is wonderful.

    - It's easy to set up cross compilation.

    - Cargo makes dependencies easy.

    - Building is almost always just `cargo build` except for very exceptional projects, same with `cargo test`.

    - `cargo fmt` makes formatting easy and built in.

    - Clippy gives actually useful lints and can automate most refactorings.

    - Rust-analyzer is fast to index decently large projects, works with almost all language features (even complex ones like proc macros), and has some real time-saver automation built in.

    Working with the borrow checker can feel like a pain, but in many cases, feels like a fun puzzle (writing hobby code, I'm not under time pressure, so it's 'solve a puzzle' and not 'oh fuck deadline coming up').

    But really, the tooling of the language is where it shines.

  • shpongled 2 years ago

    I'm building scientific software in Rust.

    Phenomenal performance, drop-in concurrency support (`iter()` -> `par_iter()` with Rayon), and a great ML-based type system that makes writing correct code easy.

    It also has some of the best developer UX/tooling out of any language I've used.

  • huyage 2 years ago

    I'm building an HTTP CRUD app on top of PostgreSQL. I have previously used Go for various CRUD apps. The experience is better except for compile time. My favorites:

    1. Much more expressive type system: no more `interface{}`.

    2. Algebraic data type with `enum`. Exhaustive check.

    3. `serde` crate is lightyears ahead of Go's `json:"wtf"`.

    4. Compile-time checks against DB with `sqlx` crate.

    5. Logging is a breeze with `tracing::instrument`.

    6. Using macros to reduce boilerplate has better UX than Go codegen.

    What these give me is more confidence in development/refactoring because the compiler can guide me most of the way.

    Low lights:

    1. Build time.

    2. Async is still clunky. e.g. Try to implement an `actix-web` extractor or middleware.

    3. Please just stablize nightly rustfmt features already.

    • mjb8086 2 years ago

      I'm looking to do a bit of CRUD on top of PostgreSQL as well. Currently mid-way through learning the language.

      Would you mind sharing some of your experiences? Do you find the 'stubbornness' of the compiler frustrating at all? How do you find the tooling?

      Also, I've considered Diesel for ORM, so was wondering if you've been using that too.

      • huyage 2 years ago

        > Do you find the 'stubbornness' of the compiler frustrating at all?

        I actually love it. The more work I can offload to compiler the better. One simple example that frustrated me in Go was adding a field to a struct. You add the field the the whole thing still compiles even though the zero-initialized value probably broke your app logic. In Rust if I add a field to a struct, the compiler warns me about all the places that I need to double check.

        > Would you mind sharing some of your experiences?

        I highly recommend zero2prod book which is well-written, practical, but still teaches the essential principles (https://www.zero2prod.com/). You basically deploy a CRUD app to DigitalOcean from scratch. The best way to ramp up IMHO.

        > How do you find the tooling?

        Cargo is sweet. rust-analyzer is all I need. I need less extraneous tooling to be effective. For example, in Go I might use a task runner to watch the repo and run tests when I change a file. But in Rust I can just follow the rust-analyzer highlights and manually compile less-frequently.

        > Also, I've considered Diesel for ORM, so was wondering if you've been using that too.

        I was not happy with GORM (https://gorm.io/index.html) and never had a satisfactory experience with any ORM. I'm a fan of writing plain SQL, even in Go. It's just that with Rust sqlx I can get compile-time checks against the schema. It's not anything new (see Haskell), but it tightens the feedback loop and I have full control of the performance.

        • mjb8086 2 years ago

          Thanks for getting back. I'll take a look at those resources you recommended. Certainly, working with Rust, a lot just 'feels right', it brings together elements I liked from other programming languages like Perl, Erlang, C. Those being: expressiveness, efficiency, a reasonably sane standard library, and functional goodies like immutability and closures.

          > You add the field the the whole thing still compiles even though the zero-initialized value probably broke your app logic.

          Ah, I found Python notorious for the same reason.

          I've found ASP.Net's ORM to be quite good, though this is only with a year's experience, so perhaps I'm missing some cracks that might emerge later.

    • rizzaxc 2 years ago

      do you have experience with protobuf in rust? that's the main thing making me think twice about using rust in backend (protobuf doesn't have an one-true rust library)

  • bsnnkv 2 years ago

    I've built the most popular[1] actively maintained tiling window manager for Windows that is available today.[2]

    It would have been impossible for me to build this in any other language. I knew absolutely 0 about developing for Windows when I started this, but I quickly realized that the Win32 API is a very old beast with a lot of footguns that Rust saves me from on a daily basis.

    On top of that, with parking_lot I can quickly and easily detect incredibly rare instances of deadlocks, and in general have to worry very little about concurrency. All of this allows me to stop worrying about the noise and focus on the real work of how the window manager should behave.

    1. Based on Github Stars

    2. https://github.com/LGUG2Z/komorebi

  • pornel 2 years ago

    Cloudflare is using Rust for network stacks, proxies, caches, compression, and image processing. Basically everything that you would use C or C++ for, but where security is also incredibly important.

  • di4na 2 years ago

    NIFs for particular casting of numbers to other binary formats in erlang. NIFS for embedded database and data format in erlang.

    Main reason ? It works. The compiler has real error messages that are helpful making the onboarding of anyone having to fix problems a far far better situation than any of the competitors. It has a proper modern toolchain.

    All of these would be killer features. All together ? No competition.

    I also write cli tool and all kind of parsers for embedded language. The libraries here, in particular if you want good error messages and UX, are simply the best in all other environment i know of.

  • kylebarron 2 years ago

    I'm building WebAssembly bindings to existing Rust libraries [0] and lower-dependency geospatial tools [1]. Rust makes it very easy to bind rust code to both WebAssembly and Python. And by avoiding some large C geospatial dependencies we can get reliable performance in both wasm and Python using the exact same codebase.

    [0]: https://github.com/kylebarron/parquet-wasm

    [1]: https://github.com/kylebarron/geopolars

    • theptip 2 years ago

      Oh, that’s really cool. Rust noob here, and I hadn't seen the tooling for building Python bindings. That looks like it could be a very powerful way to speed up your Python programs (much easier than the “just replace the slow bits in C” advice that was standard.)

      The mappings in https://github.com/kylebarron/geopolars/blob/master/py-geopo... for example look very easy to follow.

  • emadda 2 years ago

    https://table.dog (a CLI to download Stripe to SQLite).

    I considered Node.js, C#.

    I went with Rust for a few reasons:

    - Easy to build small portable binaries (as a primary language feature).

    - The type checker ensures type consistency when writing out to SQL tables (SQLite is loosely typed). Code that reads from the SQLite database implicitly benefits from Rusts strong type checks.

    - Macros to convert structs to SQL insert/updates.

    - Reduce the chance of errors at runtime.

    - Leverage as much as SQLite's write throughput as possible.

    - When converting Stripes Open API JSON spec into Rust code (using another Node.js program), the Rust type checker ensures I have a well formed HTTP client - the strict compiler makes it a good target for generated programs. Read more about this idea at (https://willcrichton.net/notes/rust-the-new-llvm/).

    • masklinn 2 years ago

      > SQLite is loosely typed

      FWIW in recent SQLite you can turn that off using “STRICT” tables, however that means the available types are restricted to INT(EGER), REAL, TEXT, BLOB, or ANY.

      ANY is also “Stricter” in strict tables: it does no conversion whatsoever, whereas in normal mode it’ll try to parse literals to numbers, and store a number in that case.

  • ostenning 2 years ago

    I'm building commercial hardware products using embedded Rust, I love it. I cant really imagine going back to C. I haven't dug too deep into the offerings with this update, but none of the standard library stuff is relevant for me

  • api 2 years ago

    ZeroTier version 2 is being built in Rust, with the main reasons being safety (it's a network protocol!), easier cross platform compilation, easier dependency management, and an all around better language than C++. It should help us greatly boost developer velocity while also be much safer from a security point of view.

    • mwcampbell 2 years ago

      That will also make it more attractive to other developers using Rust who want to embed the SDK. Speaking of which, I've emailed ZeroTier a couple of times now about licensing the SDK (the first email was last week), and haven't yet heard back.

  • cliffcrosland 2 years ago

    We are using Rust to build a fast log event database. The workload includes a lot of asynchronous networking I/O and parallelized CPU-intensive work, like indexing and SIMD-accelerated string search. There are great libraries for what we need. The performance and low memory footprint are quite useful.

  • andrew_shay 2 years ago

    I've built a p2p file transfer program [0]. All my development experience had been in Python, but I really wanted to try a statically typed, low level language, and decided to give rust a try.

    It was hard to get going, and I still only know basics, but everything just works! Typing, borrow checker, the matched results, all of that makes code bullet proof.

    [0] https://github.com/transmitic/transmitic

  • jackmott42 2 years ago

    I've done some hobby projects in Rust. One was a SIMD abstraction library, where you could write code that looks more or less like SIMD intrinsics, but it would either at compile time or runtime select the SIMD instruction set for the target cpu.

    The traits/generics/macro system made that feasible, though messy. You can do similar things with templates in C++

  • Hamuko 2 years ago

    I've written a small automatic file organiser in Rust that I run in a Docker container on my NAS. Docker reports that the container is using 1.5 MiB of memory, so that's pretty nice. Pretty sure I wouldn't have achieved that if I wrote it in Python like I normally do.

    • pdimitar 2 years ago

      That project sounds interesting and I think it aligns with what I need. Have you open-sourced it?

      • Hamuko 2 years ago

        Nope. I've been meaning to, but I feel like I need to clean up the code up a bit before I do that.

        It's also quite niche, so I'm not sure if it's for everyone's use cases.

  • orthecreedence 2 years ago

    I do a lot of cryptography stuff in rust that is ported to various platforms and embedded in larger apps so I don't have to write the same logic over and over (desktop/mobile mainly).

    Also, lately working on some identity/p2p stuff.

  • mathstuf 2 years ago

    I've used it to build two "bulletproof" services that are never intended to go down (the first catches webhook events and puts them to disk and should never go down; the second processes the on-disk files separately so that reloading the configuration can't cause us to miss webhook events). It is far nicer to have error handling as a visible part of the workflow so that you don't get "whoops, didn't consider that" situations (we had this constantly in the Python predecessor that had problems with "uh, you forgot to mention this might not be pure-ASCII, so I'm going to fall over now" problems as various fields popped up with Unicode over time).

    Best parts IMO (not in any order and not exhaustive):

    - Not having to consider exceptions (panics are still there, but are much closer to the "exceptional" behavior exceptions are/were meant to convey)

    - Explicit error handling with (usually) good error types/representation (instead of the nebulous "`err` that is a string" I've seen in what little Golang I've done)

    - Threading without worrying about race conditions

    - Dependency management

    - Data structure focused (instead of object focused)

    - The built-in `#[test]` features are also great and make it easy to test internal code without exposing it in some strange way purely for testing purposes.

    FWIW, I find that it also makes your C++ far better because now you can more easily "see" lifetime problems that people tend to write out of habit (`return stackstr.c_str();` is a…favorite that compilers now warn about).

  • solar-ice 2 years ago

    I write video streaming software in Rust. Rust helps us make sure we've got concurrency correct, has a very solid type system to help us make sure we've got general application properties correct, and seriously reduces the space where we have to go looking for segfaults and suchlike (primarily third-party C libraries and bindings to them).

  • marcosdumay 2 years ago

    I am currently writing a kinda complex GUI application intended to run on a browser. So it's in webassembly.

    Compared to Javascript, Rust safety features allow me to iterate quickly and improves my productivity from "I won't be able to complete this project" to "it's trivial, just some work". Compared to Typescript, Rust gives me some performance and types that actually represent the data I use, but I am not sure if I lost a bit of productivity (the TS tooling is horrible, so programing a bit slower isn't the entire story).

    I don't consider this a problem Rust is good for, but one that all the more suitable languages still don't support.

  • allisdust 2 years ago

    I use it to write backend web services. Some how it doesn't appear to be any less productive than typescript +nodejs combination. Library support used to be a mess but now a lot of good libraries exist to get the job done.

  • pjmlp 2 years ago

    Only hobby coding.

    Java, .NET languages and C++ are what pay the bills for me.

  • teknopurge 2 years ago

    security. I dabble with system programs and it's nice to get the speed/portability of native optimized code and know most memory issues won't occur.

  • nynx 2 years ago

    I’ve written a bunch of embedded software in rust.

mkaic 2 years ago

I'm a junior programmer who never went to college and writes Python for a living (ML researcher). I've been itching to learn another programming language, ideally something fast and low-level, and have had my eye on Rust now for some time. But in my brief experiments trying some of the docs' hello-world projects, I'm already finding myself a little out of my depth.

I'm realizing I don't know anything about compilers and very little about how languages work on a technical level -- stuff like "strong" vs. "weak" typing, memory management, stacks and heaps and garbage collection -- I've seen all those terms before but have no intuition for what they mean. I never have to think about these concepts because Python is such a high-level "it just works" scripting language.

Could anyone recommend some online resources for learning about the computer science of programming for someone with little prior experience? I'm not quite sure where to even start, but I'm eager to learn because at the moment I don't really feel like a "real programmer", more like a script kiddie. Heck, I only learned to use Git properly like 6 months ago!

  • whb07 2 years ago

    CS50X is a great starting point as they talk about the intro into bits/heap/stack etc. In the first half they have the work using C which will show you how to think about all this.

    I'd suggest doing the C parts and stopping at the python side. Once you stop, step into Rust and you'll see how much easier it is to perform all the things you were doing in C.

  • Scarbutt 2 years ago

    'The C programming language' and then 'Operating Systems: Three Easy Pieces'

aendruk 2 years ago

It’s a small thing, but array::from_fn makes me happy.

  • brundolf 2 years ago

    Something I can't figure out is how it infers the length in this example:

      let array = core::array::from_fn(|i| i);
      assert_eq!(array, [0, 1, 2, 3, 4]);
    
    Is it because of the assert_eq? Can it use the length of the second argument to infer the length all the way back up in the from_fn call?
    • dymk 2 years ago

      Yes, it's inferring that `array` must be an `[i32; 5]` because there exists an impl of `PartialEq` for `[T; N]` against another `[T; N]`, so `N` in `::from_fn` must be 5.

      • orlp 2 years ago

        Sorry but the typing part is incorrect. Look at the signature of from_fn:

            pub fn from_fn<T, const N: usize, F>(cb: F) -> [T; N] 
            where
                F: FnMut(usize) -> T, 
        
        It takes a function with signature f(usize) -> T. So |i| i gets inferred to be a function taking a usize, and returning a usize (since we just give the argument straight back). So it infers [usize; 5] in the end.

        For example core::array::from_fn(|i| i) == [0i32] wouldn't even compile.

        • wongarsu 2 years ago

          You are right. The parameter given to the closure is the array index, which is usize. Since that's returned straight back the returned value is also of type usize, and the integers in the array in the assert are inferred to also be usize.

          Which is in a way an even stronger showcase of Rust's type inference: the length of the variable is inferred from the constant array, but the type of the content of the constant array is inferred by the type of the content of the variable, which is inferred from the type of the closure.

        • dymk 2 years ago

          Good catch. The parent comment is interested in how the array length is inferred though, so I'm going to consider the explanation close enough (that it's an i32 or usize isn't particularly relevant).

      • zerr 2 years ago

        What's a sane/legit use case of such inferring in general?

        • kibwen 2 years ago

          It's not anything specific to arrays, it's just how type inference works. Here's another example:

              let mut x = HashMap::new();
              x.insert(1, 2);
          
          Note that nowhere did we need to specify the concrete type of the HashMap; the compiler sees that you eventually insert numbers into the map so it uses that information to fill in the generic type parameters for the key and value.
        • brundolf 2 years ago

          I think the one here is legit (perhaps more realistic would be where you're passing the new array to an actual function that requires a certain array length).

          People always complain about Rust's type system being verbose- well, this makes it less verbose. It knows what you need from the function call, so it doesn't make you say it. And as an added benefit, if the required type ever changes in a way that can still be generated by the provided code (eg. the function you're passing to needs a different length), you won't need to update any explicit type annotations at the call-site.

          If that's ever undesirable - you want to be very precise about your code - well, then you can do that too by specifying the generic parameters explicitly.

        • game-of-throws 2 years ago

            let numbers = [0, 1, 2];
            let doubled = numbers.map(|x| x * 2);
          
          You wouldn't want to have to explicitly type out the length of the second array.
          • tomjakubowski 2 years ago

            That isn't really inferring the size of the array though. [T; N]::map(impl FnMut(T) -> U) defines its return type as [U; N]. N is fixed at the original array literal.

            https://doc.rust-lang.org/std/primitive.array.html

            • brundolf 2 years ago

              It's inferring it from the array literal in the first place, but it is a different kind of inference from the original one. But I think that's what the question was asking for:

              > What's a sane/legit use case of such inferring in general?

              Depends how you interpret "such inferring"

            • dymk 2 years ago

              It's inferring the size of `doubled`

        • PoignardAzur 2 years ago

          A few use cases:

              let foobar = Default::default();
              do_something_with(foobar); // where do_something_with takes a FooBar param
          
          One I often use in unit tests:

              fn generate_ids<const N: usize>() -> [Id; N] { ... }
          
              let [id_button, id_label, id_thing, id_other_thing] = generate_ids();
          
          (before const generics were stabilized, the above code instead had multiple utility functions, eg generate_2_ids, generate_3_ids, etc)
        • Ar-Curunir 2 years ago

          You general type inference, or inferring the array length? The latter follows from the former, so it would be kind of a weird hole in the language if type inference worked everywhere except array lengths.

          • zerr 2 years ago

            "Scattered through multiple lines" inference :) Such code makes another programmer analyze multiple lines to deduce types in the head.

            • dymk 2 years ago

              The tooling ecosystem, for instance rust-analyzer, make this not an issue if you're reading the code in an environment that supports it. I have rust-analyzer to display inferred types when I hold `control+command`.

              In other languages, this can be a brittle/expensive operation due to meh tooling. But with Rust, it tends to "just work".

              Online viewers of source e.g. Github and such don't have all this information exposed yet, but they're incrementally getting there. For instance, "go to definition" works for Rust code in Github.

              One could imagine a generic file format included as an artifact in source control, that online viewers could consume, to annotate spans of source code. Go to definition, show expanded type, etc.

      • sophacles 2 years ago

        nit: wouldn't it be usize since the only way to get T is from the bare numbers which default to usize?

        • steveklabnik 2 years ago

          "bare numbers" don't default to usize; integers default to i32 and floats to f64.

          • aendruk 2 years ago

            It does seem to be choosing [usize; 5] in this case though.

            https://rust.godbolt.org/z/qasxnrTf9

            • steveklabnik 2 years ago

              Yes, that is true. If you use a literal array, you get i32 for the element type.

              I believe this is because of the signature:

                pub fn from_fn<T, const N: usize, F>(cb: F) -> [T; N] 
                where
                    F: FnMut(usize) -> T, 
              
              While T is unconstrained, N is a usize, which is used in the closure, which we then re-use as the element.
              • sophacles 2 years ago

                OK this is just kinda funny - I was right, but only by accident. For some reason I thought that the literal defaulted to usize not i32. The fact that this path makes my assertion of usize correct is pretty cool though. Thanks for diving into it!

          • orlp 2 years ago

            In this case the "bare numbers" do get inferred to be usize due to the signature of from_fn.

            • steveklabnik 2 years ago

              Yes, I realize that I may have read these words slightly differently than the parent meant them! I just said the same thing in a reply to your sibling.

    • aendruk 2 years ago

      Yep, you could change the asserted length and it would succeed:

        assert_eq!(array, [0, 1, 2]);
      
      If you tried both—

        assert_eq!(array, [0, 1, 2]);
        assert_eq!(array, [0, 1, 2, 3, 4]);
      
      —the assertions would never run because it wouldn’t even compile:

        error[E0277]: can't compare `[usize; 3]` with `[{integer}; 5]`
  • svnpenn 2 years ago

    Why does it arbitrarily choose length 5?

    https://doc.rust-lang.org/core/array/fn.from_fn.html

    • aendruk 2 years ago

      In that example N is inferred from the assertion; observing the output has affected it.

      • svnpenn 2 years ago

        Wow that is stupid. That is way too much assumption.

        • chrismorgan 2 years ago

          No, it’s just the right amount, and makes perfect sense. This is the whole point of type inference: where there are multiple possibilities, determine types based on how you use them, and if you still have ambiguity after that, complain.

          If you tried comparing it to something that wasn’t an array, or if you tried comparing it to multiple different-sized arrays, it would complain.

        • surewe 2 years ago

          Sure, it's a lot of assumption, but the type system would prevent an invalid assumption from compiling (arrays are typed [T; N] where T is the type they hold and N is the number of elements), and you can always override it with a turbofish.

        • Tuna-Fish 2 years ago

          There is no assumption here, just type inference. If you want it to be more explicit, you could just decorate the declaration with a type.

        • sophacles 2 years ago

          Exactly 0 assumptions are made - how is that too much?

    • shepmaster 2 years ago

      Is likely based on what you compare it to. For example, this passes as well

          let array = core::array::from_fn(|i| i);
          assert_eq!(array, [0, 1, 2, 3, 4, 5]);
      
      The magic of type inference
Lvl999Noob 2 years ago

Awesome update! It's good to see scoped threads here. Btw, can someone explain how these are safe when the initial implementation wasn't? The problem was that someone could put a reference to a local variable inside an `Rc` or `Arc` and leak it with reference cycles, right? How is that case prevented now?

  • duckerude 2 years ago

    The original implementation gave out a handle that waited for all the threads to end when it was dropped (i.e. went out of scope). That would prevent premature cleanup. But it was possible to leak the handle so that it would never be dropped even at the end of the block, and then it wouldn't wait for the threads.

    The new implementation calls a closure and waits for the threads when that closure returns. Unlike the destructor here's no way to stop that code from running when it should.

  • kam 2 years ago

    `std::thread::scope` takes a closure, and joins the threads after the call to the closure returns, rather than relying on `Drop`.

  • game-of-throws 2 years ago

    Side note: since memory leaks were deemed safe, there are easier ways to deliberately leak than using reference cycles. The most straightforward are `mem::forget` and `Box::leak`.

baby 2 years ago

so const Mutex means you can do this IIUC:

    const THING: Mutex<u64> = Mutex::new(4);
without using the once_cell crate. This is pretty cool! Also I realize that the Rust playground is still using 1.62.0

https://play.rust-lang.org/?version=beta&mode=debug&edition=...

  • shepmaster 2 years ago

    Wouldn’t you want to use static instead of const, otherwise you might get a new unique mutex at each usage? I think there’s a Clippy lint for that.

    The playground rebuilds overnight (roughly after the nightly is released); releases don’t have as fixed timing so I tend to trigger a rebuild when I notice. This HN post was delivered before the git tag was published even, so I’ve started it.

    Playground is updated now.

    • tialaramex 2 years ago

      Yes. If I say FOUR is a const Mutex with a 4 in it, whenever I talk about FOUR I get a completely new unlocked Mutex with a 4 in it, unrelated to any other Mutex regardless of whether it was made the same way.

      This might be what you want if you, for some reason, need to make different Mutexes with a 4 in them all the time. But, if you actually meant one Mutex, which intially has a 4 in it, then that's a static variable not a constant.

      https://play.rust-lang.org/?version=beta&mode=debug&edition=...

      Change FOUR to be static instead of const, and now when we print y each time it's gone up by ten because we're using the same Mutex which is what we presumably intended.

      • baby 2 years ago

        What's the difference between CONST and STATIC fundamentally?

        • notpopcorn 2 years ago

          A const is like a `#define` in C or C++, while a static is a global variable.

        • CryZe 2 years ago

          If those terms mean anything to you: const is an rvalue, static is an lvalue.

chmod600 2 years ago

With the "try_reserve" stabilizations, does that mean the "allocator_api" is closer to stabilization?

  • kibwen 2 years ago

    I think that is less about the general allocator API than it is about the fallible allocation for collections RFC ( https://github.com/rust-lang/rust/issues/48043 ), which is something that the Rust-in-Linux folks have been pushing for recently.

csomar 2 years ago

This is a huge update! At least, for me, I can see several things on my code base that could be changed because of this. (The first one is the lazy initialization of sync primitives; and the second one is BorrowedFd/OwnedFd.

rvcdbn 2 years ago

How come the first example isn’t a race on a since it’s accessed from two threads?

  • jcranmer 2 years ago

    Both threads are only reading the array. For it to be a race, at least one of the accesses must be a write.

    • theptip 2 years ago

      The second usage is a mut borrow and I think would be incompatible with a read only borrow if they were run in parallel.

      As the comment eludes to, each spawned thread is implicitly joined if you drop the join handle returned by s::spawn().

      So in this case the spawned threads run serially.

      • DenseComet 2 years ago

        Nope, the threads run in parallel. The variable x is mutably borrowed only once by the second thread. The variable a is immutably borrowed twice, so there is no issue.

        • theptip 2 years ago

          Are you certain?

          From the docs on https://doc.rust-lang.org/stable/std/thread/struct.Scope.htm...,

          > If the join handle is dropped, the spawned thread will implicitly joined at the end of the scope.

          Plus the comment says

          > // We can even mutably borrow `x` here, // because no other threads are using it.

          Which doesn’t line up with another thread having a read-only borrow on it.

          Anyway not at my machine to test right now, I could be wrong.

          • Tuna-Fish 2 years ago

            x is not used by the other thread, a is.

            > If the join handle is dropped, the spawned thread will implicitly joined at the end of the scope.

            "end of scope" here refers to the end of the closure, after the second thread is spawned.

            You can trivially show that the threads exist in parallel by adding waits.

          • theptip 2 years ago

            Oh never mind, I’m completely misreading. Thanks for the pointer.

      • tomjakubowski 2 years ago

        The second use is a mutable borrow of x, not the array. x is borrowed read-only later, after the scoped threads are joined.

    • rvcdbn 2 years ago

      ah, that makes sense thanks

  • nextaccountic 2 years ago

    If one thread tries to mutate the variable a instead of just reading from it, the compiler will output a compile-time error saying you can't do that. (But, if you wrap a into a mutex and add the required methods to lock the mutex, you suddenly can)

brundolf 2 years ago

Wow this is a juicy one

superkuh 2 years ago
  • loeg 2 years ago

    For those missing the context; this guy wants new Rust software on Debian stable’s old Rust toolchain. And complains about it in every Rust thread on HN.

    A similar problem afflicts other software in Debian stable; Rust is not unique. The reasonable solution is to stop using Debian stable if you want updates.

    • fweimer 2 years ago

      Debian packages rustc-mozilla for building Firefox. Firefox ESR 102 will need Rust 1.59.0, and that version has already been uploaded to stable-proposed-updates. 1.59 is not that old (it's from February 2022), and this actually builds after installing rustc-mozilla (with cargo even):

          use std::arch::asm;
          
          fn main() {
              unsafe {
                  asm!(
                      "syscall",
                      in("eax") 60,
                      in("edi") 0,
                      out("rcx") _,
                      out("r11") _,
              }
          }
      
      I guess for the same reason, there's now an LLVM and Clang 13 build in Debian stable, too.

      I'm not entirely happy how Mozilla forced Debian's hand here. But it means that there is a relatively current, Debian-blessed Rust compiler, and you can use it for your own builds if you are willing to upgrade from time to time.

    • pavon 2 years ago

      To clarify, without weighing in on the merits of his complaint, what he is saying is that he wants to use Debian Stable's toolchain and is complaining that the ecosystem (crates on cargo.io) move to use the latest features so quickly, and don't have stable backport releases, which makes this difficult to do.

      Edit: Reworded to remove disagreement.

      • loeg 2 years ago

        I think you're agreeing with me, but framing it as disagreement.

        > the ecosystem (crates on cargo.io) move to use the latest features so quickly

        They use the new features in new software -- implying he wants to use that new software. If he uses the old versions that predate the new features, this wouldn't be a problem for him.

        • pavon 2 years ago

          Yes, I did misread your post, sorry. Reworded mine to be a clarification rather than a contradiction.

      • Macha 2 years ago

        I'm not saying now that's opposed.

        If you want Debian to curate your compiler version, then use the Debian curated versions of your Rust software for compatibility.

    • superkuh 2 years ago

      The Debian example you're referring to was back from the release of 11. It's rustc was only 3 months old and already couldn't compile. That's not a "Debian is old" problem and it was only one instance. I've run into the same problem on other OSes as well. No OS hosted repo can keep up with rust and it's particular selection of bleeding edge types.

      And it's hardly every Rust thread. There are far too many rust threads to even count, let alone comment on.

      • msbarnett 2 years ago

        > No OS hosted repo can keep up with rust and it's particular selection of bleeding edge types.

        Seems empirically false https://archlinux.org/packages/extra/x86_64/rust/

        If your distro is unwilling to update a package with a single backwards-compatible minor version bump every 6 weeks, that’s a cultural issue, not a technical one. The “stability“ Debian imagines it gets from shipping a years-old compiler version instead of keeping up-to-date with non-breaking compiler updates is entirely mythical.

        Pick a less sclerotic distro next time.

        • superkuh 2 years ago

          Oh, okay, we'll just write off the entire concept of release based distributions because of one start-up programming language then? Everyone switches to rolling release? That's pretty absurd.

      • pas 2 years ago

        > No OS hosted repo can keep up with rust and it's particular selection of bleeding edge types.

        what? why? someone has to click the approve on the update+build+publish pipeline.. and that's in most repos anyway.

    • xvilka 2 years ago

      It's a problem for any fast moving software, not only Rust. Debian (and Ubuntu which is based on it) is notoriously slow on updating many packages.

    • baby 2 years ago

      Wait can't you just use rustup?

      • krastanov 2 years ago

        `rustup` is awesome for me as a developer. It is not convenient if I am a sysadmin that wants everything to be stable and consistent: I can not afford to use special version/package manager for every single language. It is fair to complain that rust (or python, or julia, or any other language with built-in environment/package/version manager) do not play nice with OS package managers. OS package managers are starting to take "vendoring" a bit more seriously because of this.

        • pas 2 years ago

          you want stability (aka reproducibility across a fleet of workstations/servers), but also security updates too, right? but what do you mean by consistency?

          and you don't want to create & distribute a new OS image for every security update, right?

          ... anyway, the whole problem is that only upstream can provide these kind of security updates. it's a big (and very very convenient) lie what stable distros are doing, but many times packages have to work their asses off to maintain this illusion. (when they have to patch/backport/hack packages.)

          Rust as language and compiler provides the tools for this (stability guarantee, editions, etc), but the various devs that provide Rust crates might not.

          sure, if Rust would artificially hold back new features that would definitely provide this "stability", but it's no wonder devs adopt new features fast. because they want them, it makes their life easier, and it usually makes users happier too.

          what the Rust ecosystem is doing (and NodeJS and TypeScript and quite a few high-velocity newish ones) is providing updates to users faster.

          of course there are some users that might not care about this, hence debian oldstable, and so on.

        • pdimitar 2 years ago

          I get where you're coming from but Rust is a bad example of your point. Its tooling is straightforward to install and use. I've bootstrapped several build and CI/CD servers and Node / Python have been infinitely more troublesome. I only had a problem with Rust once.

          The "stable" OS packages are mostly marketing without much substance and while they're convenient for you they also kind of lie to you as well.

        • baby 2 years ago

          I could understand any other languages, but rustup is kind of flawless from my point of view, so I'm still wondering what doesn't work exactly : o

        • rascul 2 years ago

          The OS package managers seem to be figuring it out, though. I've noticed that both opensuse and debian have a number of rust programs in their repos.

          • pornel 2 years ago

            Debian does it the hard way, by making a Debian package for every crates-io dependency used and patching Rust programs to use Debian's forked deps instead. It's seems like a massive pain for them, and has zero value for Rust end users, since normal Cargo won't use any of this.

            Rust often carries its own bugfixes to its LLVM version (before they're accepted upstream). Debian unbundles LLVM from Rust and makes Rust use Debian's LLVM, which creates a risk of undoing Rust's bugfixes and causing miscompilations.

            I don't think anyone should be using Debian-packaged Rust. Debian's and Rust's policies are fundamentally incompatible. This results in Debian having a hopelessly outdated inferior package that is a pain for both Debian users and the rest of the Rust ecosystem.

            • troutwine 2 years ago

              At the most recent RustConf William Brown spoke about how OpenSUSE deals with Rust[1]. If I remember correctly from his talk they started doing something very similar to Debian and then eventually settled on "just use rustup". I wish I had taken better notes because there was more nuance to the history there but I think they'll release the videos of the talks soon.

              [1]: https://rustconf.com/schedule#how-we-ship-rust-in-opensuse

            • superkuh 2 years ago

              We're both talking about the same elephant but we're obviously touching different ends of it. The end I'm touching smells really bad.

              The fact that so many distros have to go extremely far out of their way, breaking their standard processes, shows that Rust is unique in it's tomfoolery. You guys are advocating for the tail to wag the dog.

    • malkia 2 years ago

      or find ways to override and install latest, for example latest LLVM that would work on debian stable - https://apt.llvm.org/ (maybe similar for rust?). I've switched to this on my debian stable so that I can compile carbonlang (it needs llvm12, debian stable had only llvm11) - with above I've actually got llvm15, and then week ago switched to llvm16 (I can adjust that back to say llvm14 if I want).

      • lvass 2 years ago

        I assume the issue here is not getting the Rust toolchain on a developer system, but Rust packages in APT.

        • baby 2 years ago

          Use nix instead then?

  • msbarnett 2 years ago

    My distro gets updated software when it's available; sounds like your beef is with your distro insisting that the entire world should work on its timelines instead of vice-versa.

  • game-of-throws 2 years ago

    Why are you here flaming the Rust devs for having the audacity to dare release a new version of their software, instead of talking to the packagers that distribute years-old versions?

  • kbd 2 years ago

    > This contrasts with say, Bash developers [who] will write for what is actually available on the OSes people are running.

    This is obvious, right? Rust is compiled with what you have on your machine, while Bash is interpreted and so depends on the capabilities on the target machine.

  • bilkow 2 years ago

    > Want to use your system repo rust toolchain instead?

    If you want to use your system toolchain, just install your software from there, it will be compatible with the available toolchains, if it's a library, you don't even need the toolchain if its an application. The software is obviously also gonna be "outdated", because that's the point of Debian, stable software that has been well tested with the system.

    > This is because Rust devs, for now, are the type that will immediately beginning putting the new code features

    There is the concept of MSRV (Minimum Supported Rust Version) that a great fraction of the community follows. You just specify publicly the minimum version that your library supports and enforce it with CI. This is not as good as having an official standard (say, in Cargo.toml), but a lot better than just implicitly depending on what you think is currently available in bash for most users.

    > without consideration that it's not available in any OS repository toolchains

    Arch's package has been available on the same day in the last Rust's release. We'll probably have 1.63.0 available today or tomorrow.

    • shepmaster 2 years ago

      > This is not as good as having an official standard (say, in Cargo.toml)

      It’s present now: https://doc.rust-lang.org/cargo/reference/manifest.html#the-...

      • pavon 2 years ago

        Yeah, I was very excited when that was added. I prefer to use use the system Rust as well, and my primary complaint was the hassle of having to manually resolve dependency versions (for packages not in the OS package manager) because Cargo hasn't had great support for restricting packages to ones compatible with your version of Rust.

        There will always be some issues with package maintainers using new features without realizing it, and/or forgetting to bump the minimum version. But now that there is a standard for how it should be done, I can submit patches to these projects to fix the problems when I encounter them.

      • bilkow 2 years ago

        Oh, incredible! How have I missed that, thought it was still just a proposal...

        • moosingin3space 2 years ago

          Do be aware, though, that this is just a way to make builds fail fast if you have an older compiler. It doesn't put the compiler in a mode that makes it reject features introduced after that version. If projects want to maintain an MSRV policy, they should be using CI to confirm that they keep compatibility. This flag only exists for improved communication to downstreams.

  • nazgulsenpai 2 years ago

    This sounds more like an argument against the package manager maintainer for a Linux distribution rather than a Rust problem.

  • dietr1ch 2 years ago

    Using nix I ended up realizing that I want my system to be almost empty, providing not much more than a shell to any project I'm working on. Anything that's comes from my system is something that might bite anyone else or myself when using the project, and allow the classical "works on my machine" scenarios, luckily here at least the issue is that it won't build.

  • matthews2 2 years ago

    Gentoo got Rust 1.62.1 (the previous release) only three days after it was announced on rust-lang.org.

  • jeroenhd 2 years ago

    Ask your OS package maintainers to update their packages if you want a more modern toolkit. Or stick to the one your package maintainers decided on if you can live without those features.

    I don't think a tool that receives regular updates should necessarily come from standard OS repos, they should be stable. Perhaps the Rust people could host their own repositories but the fact they don't do it suggests that it's not worth the hassle. This stuff is handed out for free, take it or leave it.

    As Rust is open source software, you're free to grab each Rust release, package it up, and offer it as a package on your own repository, or you could probably pay someone to do it for you if you find the right people. I'd much prefer a repository as well, but I accept that the Rust devs don't work for me and I don't pay for any services that would entitle me to such requests.

  • dboreham 2 years ago

    It's a fine line. Python world spent 10 years not using Python 3 features.

    • throwaway894345 2 years ago

      Python is interpreted—you have to write code that works on all of your target platforms. Rust is compiled. You don’t have to care (much) about what is installed on your target platforms.

      • hojjat12000 2 years ago

        Except for Libc. I compiled a rust program on Ubuntu 20.04 and tried to run it on Ubuntu 18.04, and bam.... a piano fell on my head! Consequences.

        • sophacles 2 years ago

          How is this any different from C?

        • throwaway894345 2 years ago

          Yeah, that was the “(much)” in my comment. Pretty sure you can statically link some libc or another though.

  • nu11ptr 2 years ago

    Not true for most of the big hitter crates. MSRV is a big thing and I never understood why, but it was recently explained to me it was so the old tool chains in distros could keep compiling, so the big crates tend to bump MSRV very slowly. Obviously, each project owner decides this on their own, so it isn't universally true, but is common practice.

    • epage 2 years ago

      Theres also talk of improving the situation, including

      - Depencency MSRV errors explaining how to downgrade (merged)

      - The dependency resolver skipping versions that need newer Rust. To have reasonable errors, this will require the new pubgrub resolver. The main question is opt-in vs opt-out

      - Warnings when using functions newer than your MSRV

      Among other ideas

  • oxff 2 years ago

    Maybe the package managers for Linux distributions need to rethink their package distribution model :)

  • mcronce 2 years ago

    There's nothing stopping you from installing using rustup. There are also tons of projects that have much older MSRVs than simply "latest stable".

  • orthecreedence 2 years ago

    And why haven't the rust developers backported the latest versions of rust to Slackware 3.3??

  • cies 2 years ago

    > Bash developers

    Yeah those always seem to crowd my views on conferences, hordes of Bash developers /s

    Nitpick: Bash is interpreted, Rust is compiled. The executable rustc produces may well run on a machine that was not updated in the last 15 years.

ActorNightly 2 years ago

I wish they would stop adding features and just to lock down the language, and focus on more maintainability and use. Adding features is nice but its one of the biggest issues that slows down adoption. More people care about learning a language and being able to read code without trying to figure out what some latest feature does rather than some syntactical sugar.

  • brundolf 2 years ago

    Most of the features here (and in Rust updates in general these days) are just the loosening of constraints on what you could already do. If anything, these types of changes make the language conceptually simpler.

    This gets pointed out by somebody in the comment section under every Rust update. The trope that Rust is getting harder to use is tired, and not really based in reality.

    • ActorNightly 2 years ago

      Different syntax = different ways of doing things = codebases that diverge in implementation. It doesn't matter if the new way doing things is conceptually simpler, you are now creating 2 ways to do things based on what version of the compiler you use, which people will have to know, therefore increasing complexity, not reducing it. I don't understand how people don't get this by now. Saying "oh just learn and use the latest version" is completely unrealistic guidance.

      And while its fine for high level scripting languages like Python, because the whole idea is to abstract a lot of the functionality into the syntax, the issue with Rust is that its trying to be the next C and be applicable for writing critically important pieces of software like parts of the linux kernel. You absolutely must have a locked down language set for this. The reason C is still used for the kernel isn't because of circumstances, its specifically because there is very few ambiguities in the core language.

      • brundolf 2 years ago

        > you are now creating 2 ways to do things based on what version of the compiler you use

        Which of the features in Rust 1.63 adds a divergent way of doing something you could already do?

        > The reason C is still used for the kernel isn't because of circumstances, its specifically because there is very few ambiguities in the core language

        What are some ambiguities in the core Rust language?

      • game-of-throws 2 years ago

        C was used because it existed 30 years ago, and is full of ambiguities (undefined behavior), and Rust curtails UB quite a bit by defining many semantics that are left unspecified in C.

        You don't need a "locked down" language to build an OS. In fact Linux just upgraded the version of C they use to C11. Time always marches forward, my friend.

        • sophacles 2 years ago

          And C is about to get C23 with additionaly syntax and features.

      • mbrubeck 2 years ago

        Which "different syntax" are you talking about? I don't see any changes in this release that add new syntax to the language.

  • Macha 2 years ago

    There's no new syntax here?

    The first is a new library function that takes a closure as the parameter. This function has existed for as long as Rust has been 1.0 in the crossbeam library, it's just now also in std.

    The second change is a new api type.

    The third change is a library change to increase flexibility of that library.

    The fourth is arguably a bugfix, where existing syntax could not be used in an edge case.

    The fifth is unifying the compiler implementations so that some types of patterns now work in code using the oldest compatibility mode that previously only worked in more current modes.

  • ekidd 2 years ago

    Almost everything in Rust 1.63.0 appears to be an improvement in the standard library. Scoped threads no longer require a third-party library, some types support "const" better, and a bunch of existing types got some useful new methods. The only real language change here is the "impl / turbofish" one, which allows something that should work, but which wasn't allowed before.

    I'm not sure it would be realistic to totally stop adding features to the standard library, or to stop making existing syntax work the way a Rust user would expect. I would hope that any actively maintained language does things like these.

  • game-of-throws 2 years ago

    Literally every single thing in the post is generalizing a feature that already exists. Don't you think removing these special cases makes the language simpler and easier to use?

    You might have a point though in a few releases when GATs are stabilized. I'll keep an eye out for your username complaining about that release ;)

  • funklute 2 years ago

    Rust is a pretty young language though.... if now is not the time to add features, then when is?

  • pas 2 years ago

    there are a lot of work-in-progress features that are important so we can write more maintainable code, like GAT.

    but it's also true that there are quite a few threads that are conceptually simpler than GAT and are just left unfinished.

    and there is a very serious push (or pushback with regards to GAT) to keep those promises.

    https://github.com/rust-lang/rust/pull/96709#issuecomment-11...

  • oxff 2 years ago

    I wish they'd break their current backwards compat. promises to fix some accidental complexity that has been introduced. Let legacy langs provide this kind of maintainability

    • tialaramex 2 years ago

      It's not worth it.

      For example, it seems as though "Word".contains(char::is_ascii_lowercase) should work, because after all "Word".contains(char::is_lowercase) works and char::is_ascii_lowercase exists and does what you expect, the same but for ASCII only.

      However, for compatibility reasons char::is_lowercase takes a char, while char::is_ascii_lowercase takes a reference to a char, and so you can't just pass it to contains() and will need to write a closure to pass to contains to do ASCII instead.

      Writing the closure is ugly, but it's nowhere close to how awful a compatibility break would be so even a million things like this (and so far maybe I can think of a dozen) aren't worth it.

    • brundolf 2 years ago

      They have the Editions mechanism for unavoidable breaking changes, but their target market is people who care a whole lot about stability and maintainability, so it wouldn't make any sense for them to jettison that design goal.

      • SAI_Peregrinus 2 years ago

        Unfortunately the Editions system (as nice as it is) only works for the language, not for the standard or core libraries. It's possible to create such a system (similar to glibc symbol versioning), but AFIAK Rust doesn't currently have one.

    • cercatrova 2 years ago

      It's a systems language so they're not gonna break BC. Although if there's a really great feature that comes out that requires BC breaking, then they should do so.