Settings

Theme

Rust is for Professionals

gregoryszorc.com

138 points by indygreg2 5 years ago · 156 comments

Reader

skohan 5 years ago

Rust is currently my favorite language for personal projects. It's got a very good value proposition in terms of giving some high level features along with low level control and performance, great compatibility story, and the tooling and community is absolutely great.

However, as the manager of a technical team, I would not choose it for professional projects. The learning curve is very steep, and to reap the benefits you have to pay a high cost in terms of accepting additional complexity. I think Rust is a good choice for some professional use-cases, for instance performance-sensitive applications like embedded, or safety-critical applications. But for many applications, like your average webserver for example, I believe you will lose productivity and have a hard time hiring if you choose Rust.

For my team, we chose Go because it is easy to hire and onboard people, the tooling and compatibility story is plenty good enough, and due to the complexity ceiling, there's only so much damage a developer can do in terms of taking the codebase in a bad direction.

Rust is a capable language for a professional setting, but it is also a language ideally suited for people who enjoy indulging in complexity and tricky problem solving, and this is not the right choice for every project or team.

  • sylvain_kerkour 5 years ago

    As an indie developer, switching from Go to Rust for web APIs was extremely satisfying. At first there was a productivity loss, but after a few months the productivity was better than Go thanks to Rust's functional features which make it extremely pleasant to write business logic, and the type system catching bugs during development instead of production.

    You can read more about the experience here: https://kerkour.com/blog/rust-for-web-development-2-years-la...

    The only thing I'm missing is Go's awesome TLS support (with autocert & co).

    That being said, I understand your point regarding hiring: a friend of mine totally refuses to learn Rust because the syntax looks not good to him.

    • novok 5 years ago

      How much of a productivity boost would you’ve gotten from java or kotlin which provides a lot of those features with all the complexity and build times?

      • sylvain_kerkour 5 years ago

        I did a bit of Java and Kotlin for Android development, but never for web APIs so I can't really tell, but one thing I'm sure is that Rust's build system and dependency manager (Cargo) is far more pleasant to use than the experience I had with Java.

        Other great things are the ease of packaging with Docker, the low resources usage which helps to keep Heroku's bills small, and the excellent IDE support.

    • peppery-idiot 5 years ago

      For web work, are there benefits to using Rust over a garbage collected language that also benefits from a similar type system?

      • sylvain_kerkour 5 years ago

        Do you have a specific language in mind?

        Honestly, I don't feel that I spend time managing memory in Rust: I use ARC pointers for long-lived shared object (Database connections pool, mailer...) and otherwise the ownership is pretty straightforward during the lifecycle of a request, data is moved from the top layer (HTTP handlers) to the bottom (Repositories to access Database) and back for the response.

        • peppery-idiot 5 years ago

          From my limited knowledge, Rust's type system is quite close to the ML family-- so perhaps F# would be a general-purpose GC'd language equivalent?

          I am mainly interested to know how much overhead time is spent appeasing the borrow checker and managing memory that would otherwise free up mental cycles if a GC were available . The async story for Rust also seems confusing (but I hold my hands up and plead ignorance on this count).

        • skohan 5 years ago

          > I use ARC pointers for long-lived shared object (Database connections pool, mailer...)

          Does that mean you basically enforce sequential database reads? Seems like a bottleneck if your server is concurrent

          • masklinn 5 years ago

            Arc just allows sharing between multiple threads, it doesn't prescribe any sort of locking.

            If they're sharing a connection pool then each HTTP request would get its own connection out of the pool, and they'd be concurrent (access to the pool may or may not be serialised depending on the pool's details, sqlx is internally mutated not externally locked for instance).

            The mailer might be behind a mutex (its access completely serialised), or the "mailer" might just be the input side of a queue / channel, and the actual mailing work be done in a separate process (that seems way more likely than bounding the request on sending emails really).

  • twobitshifter 5 years ago

    “Complexity is the enemy of execution.” - Tony Robbins

    I don’t think Tony ever did any coding, but you’re right that it still applies here.

    A google search shows complexity has many other enemies: security,reliability, agility, and progress itself.

    In most cases I think this is right, use Go and simple tools whenever you can. There are cases where complexity is unavoidable though, like when you’re trying to land a rover in a precise location on Mars, where the sky crane makes sense, but don’t introduce the complexity when it’s unneeded.

    • justsomeuser 5 years ago

      Rust is more complex than Go, but when you are dealing with the inherent complexity in a problem, sometimes Rust can allow you to make the decision to take a larger chunk of that complexity and handle it at compile time (instead of at runtime with a crash->debug->fix feedback loop).

      So you get sometimes stressful and frustrating writing sessions, coupled with more confidence in the runtime correctness. Also the more expressive type system can allow you to more closely match the domain, which helps when you add new features in the future as you can rule out certain invalid states or interactions.

      I often feel you need to know the right conventions to write correct Go programs, where as Rust will just tell you you cannot run it.

      I would get things done faster in Go for sure, but the compiled artifact would likely have more issues during runtime.

    • kbenson 5 years ago

      > don’t introduce the complexity when it’s unneeded.

      Prediction is hard. Sometimes I start with something simple, and it ends up growing in scope until it's quite large and complex. Other times I start with something simple, and it stays simple.

      So, what's worse, using a complex and/or strict language for something simple, or a simple and less strict language for something that ends up being very large and/or complex?

      Interestingly, some people will probably read that last paragraph as my trying to make a point through implication. I suspect different people will assume I'm saying completely opposite things based on their own experience. Really, it's just hard, you get better at making the right choice as you go along, but you'll never always get it right.

  • kzhukov 5 years ago

    > safety-critical applications

    Nope, Rust is also not suitable for these tasks since it doesn't have a certified toolchain (e.g. ISO26262 for automotive or DO-178 for aerospace).

    > embedded

    Current LLVM-based compiler lacks support for some platforms (e.g. Xtensa for some ESP32 MCUs).

    • wyldfire 5 years ago

      'Embedded' doesn't require supporting everyone's favorite microcontroller. Even if ESP32 is really popular, there's plenty of others. OBTW experimental xtensa backend is landing in upstream llvm lately.

      • kzhukov 5 years ago

        That were just the examples when Rust is not really suitable as a drop-in replacement for C/C++. Of course, if your platform is well-supported by Rust compiler and you don't have to write safety critical applications, then using Rust is just a matter of choice.

        I don't argue whether it's good or bad language. It's a quite interesting one with its own pros and cons. If it fits you, then it's great, but you should know its limitations.

    • fzzzy 5 years ago

      It took me two hours to compile the esp32 fork of rustc on a 2015 macbook pro. Not ideal, but it works just fine.

    • gte525u 5 years ago

      Certified toolchains only factor into play in DAL A for DO178. Either that or manually inspecting the compiler output and tracing it to the source.

  • gher-shyu3i 5 years ago

    Rust and Go are not in the same domain space, so it's a false dichotomy to compare the two.

    It seems in your use case, Java would have been a viable (and potentially superior) alternative to golang.

ahelwer 5 years ago

Here I am on day three of an attempt to modify a rust program with logic that would have taken about twenty minutes to implement in C# or Java. It has to do with operations on strings (use a regex to find a string in some text, escape all the regex-reserved characters in that string, then use the string as a regex to find other occurrences of itself in the text) so I get that I'm really thrown into the borrow-checker deep-end, but man writing rust feels like working on one of those puzzles where you try to fit a set of tiles inside a rectangle. You'll almost get it but then some edge is sticking out. So you move the tiles around but this leads to two edges sticking out now! So you do a whole bunch of additional exploring before ending up right back where you started with the one edge sticking out.

I even abandoned the learn-by-stackoverflow-search approach to rust and read the first seven or so chapters of the rust book. But even that hasn't helped very much. I know I am just currently in the painful, frustrating stage of learning where nothing really makes sense, and at some future point it will all click, but it really can't be overstated just what a wicked learning curve this language has.

  • hardwaregeek 5 years ago

    I don't know if this helpful, but from your description of your problem, here's how I'd do that in Rust:

    - Use a regex to find a string in some text: use regexes, get a &str

    - Escape all the regex reserved characters in that string: Okay this requires mutation but we can't mutate a &str, let's loop over the &str's characters (using the Chars iterator) and push them into a new String with the proper escapes

    - The new String we own, so we can easily turn it into a regex and use it to search the original string.

  • ajross 5 years ago

    That's where I always end up when I start playing with Rust too. At this point I genuinely think I've given up on belief that the "borrow checker" paradigm is ever really going to be broadly successful (vs. "screw it, just do it in <other managed language>").

    Problems with well-constrained allocation paradigms do really well in rust. Problems with reading/parsing/storing messy data structures from external data just really, really hurt.

    I think a big part of the problem is that the ownership analysis layer has the dual crises of being (1) really complicated and too hard to keep in your head as a single model and (2) specified in an ad-hoc way that resists formal rules. So eventually you end up in of those puzzles like you mention where... there's just no answer! No one's been where you have, and the lack of rigor means you end up trying to reverse engineer the compiler front end trying to find the trick that works. And then you give up and write it in Go or Python or whatever.

    • sanxiyn 5 years ago

      No, borrow checker's formal rules are rather simple. Here it is:

      It is an error to access a place, when an access conflicts with a loan, and the loan is live.

      • ajross 5 years ago

        You're describing the desired behavior of the compiler, but not what it actually implements.

        In fact there are an infinite number of correct Rust programs which will never access memory incorrectly but which will still be rejected by the compiler, for the simple reason that Rust's authors, talented though they may be, have made no progress at all at solving the Halting Problem.

        What Rust actually accepts is a subset of correct programs. And this subset is somewhat informally defined as "whatever the compiler could manage to prove". Real code hits against this limit occasionally, and when it does there's really no option other than "join the Rust team" or "try voodoo".

        • estebank 5 years ago

          There are two problems here. Rust accepts a subset of correct programs that involve borrowing, that's absolutely true, but it gives you the tools to get around that: you either use Arc<Mutex<T>, where now you are in the same space that any refcounted GC language is in (with the extra flexibility that if you can "choose your guarantees"[1]), or use unsafe which lets you dereference memory freely in the same way that you would in C or C++.

          The unstated problem is that there are patterns from other languages that are actually invalid and Rust is correctly denying, but not properly communicating it, which causes frustration. You can always make the argument that the failure modes are benign or rare, and for those cases noone is stopping you from actually using unsafe.

          [1]: https://manishearth.github.io/blog/2015/05/27/wrapper-types-...

          • ajross 5 years ago

            > The unstated problem is that there are patterns from other languages that are actually invalid

            And this is why this argument persists. This is, to people outside the community, a semantic evasion:

            1. It relies on a Rust-internal definition for "actually valid" (conforming to Rust's specific set of provability requirements) that doesn't correspond to what the rest of the world views as "correct" (not behaving incorrectly). Think about stuff like allocate-through-a-session-and-free-in-a-block paradigms (Apache was famous for this), or run-once-and-exit, or garbage collection, etc... Those things aren't "invalid" in any reasonable sense, they're just not what Rust programs do.

            2. The definition for "valid" is (and this is my point above) entirely Rust-internal and ad hoc. It's not that we refuse to conform to your rules, really, it's that we don't know what they are!

            • steveklabnik 5 years ago

              There's an anecdote about a similar effect in the article too:

              > Fun fact: while at Mozilla I heard multiple anecdotes of [very intelligent] Firefox developers thinking they had found a bug in Rust's borrow checker because they thought it was impossible for a flagged error to occur." However, after sufficient investigation the result was always (maybe with an exception or two because Mozilla adopted Rust very early) that the Rust compiler was correct and the developer's assertions about how code could behave was incorrect. In these cases, the Rust compiler likely prevented hard-to-debug bugs or even exploitable security vulnerabilities. I remember one developer exclaiming that if the bug had shipped, it would have taken weeks to debug and would likely have gone unfixed for years unless its severity warranted staffing.

              Sometimes people think that they're right and that the compiler is wrong, but often, (not not necessarily always!) it's that they forgot some important piece of context.

              (Oh, and Rust absolutely can do "allocate in a block and free at once" stuff, or "run once and exit" stuff...)

            • sanxiyn 5 years ago

              Rules are actually simple and not ad hoc. I in fact think Rust documentation should state rules and make them more prominent. In my experience, knowing the rules does not help you to keep them, but at least we will get less complaints of the sort "we don't know what the rules are".

              To recap, here are the rules:

              It is an error to access a place, when an access conflicts with a loan, and the loan is live. Access means source code construct to read or write, whether the construct is actually executed in runtime is irrelevant. Place is static approximation of a set of bytes in the memory. Place is either local (x), field (x.f), or upvar (local captured in closure). Indexing (x[i]) and method calls (x.m()) are approximated as local (x). Loan is either shared (&x) or mutable (&mut x). Read access conflicts with mutable loan, write access conflicts with all loans. Expression is live from the definition to the end of the containing statement. Declaration is live in connected region of control flow graph from the definition to potentially multiple last uses.

        • sanxiyn 5 years ago

          No, the rule I stated is what is actually implemented.

          Like all type checking technology, the rule is about static behavior, not dynamic behavior. So halting problem is irrelevant.

          Rust accepts all programs that follow the rule in static behavior. It's not a subset, and it's not a best effort to prove. It's the complete statement.

          "Access" means source code construct to read or write. Rust doesn't care whether the construct is actually executed in runtime.

          • estebank 5 years ago

            I believe the "subset of correct code" refers to things like the current non-support for things like

                let (left, right) = (&mut foo[..split], &mut foo[split..]);
            
            where you have to rely on things like foo.split_at_mut(split), which are implemented as unsafe under the cover.

            I see these are limitations but not show-stoppers in any way.

            • ajross 5 years ago

              It's surely not a showstopper when it's on one line, but again the problem in practice is that those slices may be other operations or hidden behind other APIs, and happening at different depths in a complicated call tree, and you get an error only up at the top when what you're doing looks "clearly correct".

              Yes, sure, the rule in question may be simple in the abstract (it's a compiler, after all -- they're just software doing straightforward things). But the ability for the poor programmer to detect which rule is being violated where is a lot more limited than the compiler is designed for. Thus the user with the upthread complaint, which is hardly unique.

              I mean, at the end of the day if Rust wants to be an everyday language for this kind of everyday problem, the analysis paradigm needs to be communicated much better to everyday hackers (via docs, error messages, whatever). When Rust was new this seemed like just a technical problem to be solved with software maturity. I guess at this point after several years of regularly returning to play with Rust and being frustrated every time, I've mostly given up.

              • sanxiyn 5 years ago

                Sure, but you claimed there is no simple formal rule. That's simply false. It's just that the formal statement is not front and center in documentations, because it is not very useful to learn the rule, and most documentations are targeted to learners.

            • sanxiyn 5 years ago

              Yes, but the rule is still complete. What is going on is "place" is currently defined to be local x, or field x.f, or index x[i], but all x[i] are merged to x[*]. Rust's so-called NLL changed definition of "live". The point is, the rule is simple, formal, and complete. It is neither an ad hoc rule nor a best effort prover.

              • ajross 5 years ago

                > What is going on is "place" is currently defined to be

                Not to belabor this growing thread, but here's the disconnect. I complained the rules were ad hoc and complicated, you replied that they were simple, and when challenged on an edge case your treatment is to add another clause to re-define a term you used in isolation earlier.

                That's what "ad hoc and complicated" means.

                • sanxiyn 5 years ago

                  "place" and "live" have their respective intuitive definitions. To clarify, "place" is a set of bytes in the memory. "live" means it is used again. But both are runtime concepts, so Rust compiler needs to compute static approximation of these runtime concepts. This static approximation is what is complex and changing. But the principle is simple.

                  Actually, my elaboration of "place", local/field/index, is nearly complete. The only thing missing is upvar, which is local captured in closure. Pre-NLL "live" is simple: expression is live from the definition to the end of the containing statement, and declaration is live from the definition to the end of the containing block. Post-NLL "live" is connected region of control flow graph from the definition to potentially multiple last uses. Really, that is the whole story.

                  There are two changes currently in development. One changes definition of "place" to include field captured in closure, in addition to local captured in closure. The other is more drastic, changing definition of "live": a loan is live where the loan is origin of a variable and the variable is live. This is great, because current approximation is equivalent to union of all origins.

          • ajross 5 years ago

            Link to the relevant spec then? Admittedly my intuition is a year stale at the moment, but I went looking hard for this at one point and found nothing other than source code.

            • sanxiyn 5 years ago

              No spec, because technical details are complex, but spec, when it is written, will just be an elaboration of "It is an error to access a place, when an access conflicts with a loan, and the loan is live". This has never changed since Rust 1.0, more than 5 years. Spec will just need to define "place" and "live" and that takes time.

            • iudqnolq 5 years ago

              There's no spec yet, but there are working groups working on it.

  • CJefferson 5 years ago

    While this can be painful, my basic suggestion -- clone more. The temptation is to never clone, butin C++ people run copy constructors all the time without thinking about it (as they are called automatically).

    • wyldfire 5 years ago

      I clone all the time to make the borrow checker leave me alone. If this code shows up as a hot spot in a profile I can spend the extra energy to optimize it.

      I hope to evolve my understanding so that I can do the right thing at design time to minimize unnecessary clone()s.

    • ocschwar 5 years ago

      Cloning means runtime inefficiency, but the cost is so pathetically small compared to the amount of code we run in Python and Java with their larger footprints. Yes. Clone more.

      • brundolf 5 years ago

        This is one of the psychological traps I've experienced writing Rust: in Python or Java you can't hope to make things more efficient at a certain point. You couldn't get clever with string re-use if you tried, etc. So you're content to just move forward and be practical.

        Whereas in Rust you know that it's probably possible to use a &str there instead of a String. If you just bang on it a little longer, you can make it just a bit more optimal. The clone()s and the Vec::new()s are all explicit, making them feel heavier than those other languages, when in reality they're still quite a bit lighter.

        You usually don't have to optimize them out! Your code will probably be faster than Java even with a bunch of clones, etc! But there's this temptation that's really hard for programmers to resist, to sink hours and hours into making things just slightly more optimal given the opportunity. You have to consciously talk yourself out of that if you want to be productive.

        • masklinn 5 years ago

          > The clone()s and the Vec::new()s are all explicit, making them feel heavier than those other languages, when in reality they're still quite a bit lighter.

          That's… not exactly true. `Vec::new` is completely free so that's a different debate, but much as in C++ or C allocations in Rust are much more expensive than in managed languages:

          1. system allocators genuinely suck, all of them, though some more than others (iirc macos' is especially bad)

          2. managed languages can much more easily specialised allocation strategies (freelists, bump allocators, type-custom allocation strategies), this is either difficult / impossible (no custom allocators) or way more painful (manually pass custom allocators in) in Rust

          As a result, allocations in Rust are really tremendously slow by default, it's not too rare to see questions about Rust programs which are slower than managed equivalents, in release mode. Because the rust program is allocation heavy (relatively), it might have 10% the allocations of the non-rust program but the allocations are 100 times more expensive.

          • brundolf 5 years ago

            Interesting, I hadn't heard this before. Do you have any resources I could read?

            Edit: I do know that languages where strings are immutable use that fact to do lots of optimization (automatically sharing "copied" strings and just cloning reference-counters, for example), but you can accomplish some of this in Rust too with Rc<> or even persistent data structures if you really want to. And of course there are cases where it's more efficient to actually mutate a string, which these languages can't do. But it sounds like you're talking about something else?

        • pdimitar 5 years ago

          > You have to consciously talk yourself out of that if you want to be productive.

          That's 100% correct but there's one problem: how do you write down your future improvement opportunities?

          Skip 2 optimizations here, 3 optimizations there and after 30 PRs you can easily have notes spanning 100 bullet points on what to optimize one day in the future Soon™.

          I am still not at what I'd call an expert level in Rust -- and the skill ceiling is quite high -- so maybe with time I'd start to automatically spot these optimization opportunities?

          But before I am at that level I'd prefer to either keep hugely long notes or, ideally, have a tool recommend optimizations to me.

          • SAI_Peregrinus 5 years ago

            The simplest tool is to just grep for various constructs that copy or allocate. Clippy, the Rust linter, will find some of these (as well as other issues, some of which tend to cause performance problems).

            The better tool is to profile the code, then optimize hotspots. There's a Rust Performance Book with a list of profilers known to work with Rust programs[1]. Rustc does support Profile Guided Optimization[2] which is often pretty good at speeding up the output even without any code changes.

            [1] https://nnethercote.github.io/perf-book/profiling.html [2] https://doc.rust-lang.org/rustc/profile-guided-optimization....

            • pdimitar 5 years ago

              Helpful links, thank you!

              > The simplest tool is to just grep for various constructs that copy or allocate.

              Do you happen to have a curated list somewhere?

              • SAI_Peregrinus 5 years ago

                For allocations (more expensive in many cases) the Rust container cheat sheet[1] is helpful. Box, Vec, Rc, Arc, and Mutex are the standard types which allocate.

                Not sure of a list for things that copy their data. The Clone trait is the obvious one, and it requires calling clone() to make the copy. EG `val.clone()`. So searching for `.clone()` will get you those. But other things like to_string are expensive, and From/Into are sometimes expensive.

                [1] https://docs.google.com/presentation/d/1q-c7UAyrUlM-eZyTo1pd...

      • rogers18445 5 years ago

        The vast majority of the code you write is glue code, you can be as inefficient when writing it as you want and it doesn't matter. Optimize only what the profiler tells you your cpu is spending real time in.

      • xena 5 years ago

        Doesn't the compiler optimize out clones when it can?

        • toby 5 years ago

          There's a Cargo tool called "clippy" which will tell you if it finds unnecessary clones.

        • tylerhou 5 years ago

          Probably not; detecting unnecessary clones is expensive for non-trivial cases. If a human can't prove that the clone isn't necessary (by writing the code in such a manner that the borrow checker accepts), I doubt the compiler could.

        • sanxiyn 5 years ago

          No, not really.

    • nemetroid 5 years ago

      I've never seen C++ that isn't cautious about copying as little as possible.

      • masklinn 5 years ago

        A big difference is that C++ copies implicitly. Rust has the opposite default, it will never clone implicitly.

    • dman 5 years ago

      People do think about this all the time in C++ and it is very common to get code review comments about avoiding copies if you overlook such things.

      • Koshkin 5 years ago

        Yup, and I wish the compiler would give a warning on a use of a copy constructor...

  • volta83 5 years ago

    > use a regex to find a string in some text, escape all the regex-reserved characters in that string, then use the string as a regex to find other occurrences of itself in the text)

    This took 2 min: https://play.rust-lang.org/?version=stable&mode=debug&editio...

    and I don't recall what's the last time I used the regex crate.

    I'm certainly doing all the regex stuff incorrectly, no idea how to match regex-reserved characters, no idea if I'm using the iterator API correctly (its the second example in the regex docs..), etc.

    But the idea is simple, search the text for the regex, escape all the reserved characters, use that regex to search for occurrences of the same regex.

    I guess that if you want a fully-vectorized, zero-allocation, zero-copy, multi-threaded, gpu... version of this that maxes out the theoretical peak of the hardware available, then you'll probably end up putting a considerable amount of work here.

    But otherwise, for someone with literally 30 seconds of experience with the regex crate, it was as straightforward to implement as you mentioned. Just c&p their second example, and just added some glue. This is how I'd implement this in Python, or Java...

    EDIT: other responses suggest cloning, but that seems to suggest that one has to explicitly and deliberately think about cloning. I instead just played a game of type golf using functions like `.to_string()` or `.collect` to "clone" stuff behind the scenes. My code is probably horrible, but is the first thing that came to mind.

  • sanxiyn 5 years ago

    You can't learn Rust by StackOverflow, that just doesn't work. It is well known, I repeat this everytime I see it happening, and they never listen. It's deeply frustrating.

    • api 5 years ago

      You can't learn any language by StackOverflow, but some languages are more forgiving to people who don't actually know them.

      If you don't really know Rust and in particular understand the borrow checker and how it works (and why it exists), you are going to have a bad time.

      Rust in general has a learning curve, but once you do learn it there is a payoff. If Rust code compiles (and if you didn't use 'unsafe'), you can be pretty damn sure it is free of a long list of potential bugs including concurrency bugs, buffer overflows, null pointer dereferences, alignment problems, double-free, use-after-free, stack smashing, most memory leaks, and so on. The runtime cost of that assurance is less in Rust than in any other language I know, and sometimes the safety of Rust actually lets you do things in higher performing ways that would be dangerous to code in C. In C/C++ you have to knit your own straightjacket, and yours may not be as well thought out or efficient as the one the Rust compiler issues you.

      I've really started to appreciate Rust's safety. If the code compiles there can still be bugs, but they're going to be higher up at the logic/algorithm level instead of the annoying sorts of bugs that constantly crop up in C/C++.

    • ahelwer 5 years ago

      I mean yeah, that's why I went and read a bunch of the book. But I still run into lots of issues. Like what the hell is a borrow cow and when would I want to use it? And how do I fix the "temporary value dropped when borrowed" error in a series of maps on options?

      • skohan 5 years ago

        I would recommend asking questions on the subreddit. There's a standing thread for asking questions, and people usually give high-quality help and respond quickly

      • estebank 5 years ago

        If you don't mind sharing snippets for the diagnostics you couldn't figure out, I would love to see them in case we can mechanically suggest the appropriate code, but in general a well placed .clone(), .cloned() or .collect() will be what you want. Likely what's happening is you're iterating over a sequence (you can think of options as a sequence of only one iteration as well), but that iterator is over borrows of the sequence's data, instead of owned values. If you try to return that outside of its enclosing function, you will see that error.

        As for a "borrow cow", you probably want to read https://doc.rust-lang.org/std/borrow/enum.Cow.html and https://deterministic.space/secret-life-of-cows.html. For what you are doing, it is an optimization to avoid unnecessary clones that is absolutely not required: make your code work with String and worry about Cow<'_, str> later.

        • ahelwer 5 years ago

          Sure! So I'm trying to implement this feature: https://github.com/tree-sitter/tree-sitter/issues/982#issuec...

          And here's my branch (you can see the latest commits to see the file I'm modifying): https://github.com/ahelwer/tree-sitter/tree/testfile-separat...

          I haven't had a chance to go through and add clones everywhere, and will be away at a PT appointment for the next hour or so, but would appreciate any pointers you can give.

          • devit 5 years ago

            It passes cargo check with this change.

              diff --git a/cli/src/test.rs b/cli/src/test.rs
              index ac4807bf..8b9882ab 100644
              --- a/cli/src/test.rs
              +++ b/cli/src/test.rs
              @@ -410,16 +410,17 @@ fn parse_test_content(name: String, content: String, file_path: Option<PathBuf>)
                       .map(|b| String::from_utf8_lossy(b).to_string())
                       .map(|s| escape_reserved_regex_chars(&s));
                   
              -    let suffixHeaderPattern : Option<String> = suffix
              +    let suffixHeaderPattern : Option<String> = suffix.as_ref()
                       .map(|s| String::from(r"^===+") + &s + r"\r?\n([^=]*)\r?\n===+" + &s + r"\r?\n");
                   
              -    let suffixDividerPattern: Option<String> = suffix
              +    let suffixDividerPattern: Option<String> = suffix.as_ref()
                       .map(|s| String::from(r"^---+") + &s + r"\r?\n");
               
              -    let headerRegex = suffixHeaderPattern
              -        .and_then(|s| ByteRegexBuilder::new(&s[..]).multi_line(true).build().ok())
              -        .as_ref()
              -        .unwrap_or(&HEADER_REGEX);
              +    let headerRegexFromSuffixHeaderPattern = suffixHeaderPattern.as_ref()
              +        .and_then(|s| ByteRegexBuilder::new(&s[..]).multi_line(true).build().ok());
              +
              +    let headerRegex = headerRegexFromSuffixHeaderPattern
              +        .as_ref().unwrap_or(&HEADER_REGEX);
               
                   // Identify all of the test descriptions using the `======` headers.
                   for (header_start, header_end) in headerRegex
            • ahelwer 5 years ago

              Thank you for this! the .as_ref() seems to solve a lot of the problems, since I guess just doing a straight .map() takes ownership of the contained string.

          • estebank 5 years ago

            The error output for your code is

                error[E0382]: use of moved value: `suffix`
                   --> cli/src/test.rs:416:48
                    |
                406 |     let suffix = FIRST_HEADER_REGEX
                    |         ------ move occurs because `suffix` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait
                ...
                414 |         .map(|s| String::from(r"^===+") + &s + r"\r?\n([^=]*)\r?\n===+" + &s + r"\r?\n");
                    |          ------------------------------------------------------------------------------- `suffix` moved due to this method call
                415 |
                416 |     let suffixDividerPattern: Option<String> = suffix
                    |                                                ^^^^^^ value used here after move
                    |
                note: this function consumes the receiver `self` by taking ownership of it, which moves `suffix`
                   --> /Users/ekuber/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:451:38
                    |
                451 |     pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
                    |                                      ^^^^
                
                error[E0716]: temporary value dropped while borrowed
                   --> cli/src/test.rs:419:23
                    |
                419 |       let headerRegex = suffixHeaderPattern
                    |  _______________________^
                420 | |         .and_then(|s| ByteRegexBuilder::new(&s[..]).multi_line(true).build().ok())
                    | |__________________________________________________________________________________^ creates a temporary which is freed while still in use
                421 |           .as_ref()
                422 |           .unwrap_or(&HEADER_REGEX);
                    |                                    - temporary value is freed at the end of this statement
                ...
                425 |       for (header_start, header_end) in headerRegex
                    |                                         ----------- borrow later used here
                    |
                    = note: consider using a `let` binding to create a longer lived value
            
            
            You need to change line 413 to turn `suffix` into an `Option<&str>` to avoid taking ownership of it:

                let suffixHeaderPattern: Option<String> = suffix
                    .as_ref()
                    .map(...);
            
            and you need to change the `headerRegex` extraction to turn it also into an `Option<&str>` from an `Option<String>` by using `.as_ref()` before the `.and_then` call, which lets you avoid the `&s[..]` reborrow, that only lives until the end of that closure:

                let headerRegex = suffixHeaderPattern
                    .as_ref()
                    .and_then(|s| ByteRegexBuilder::new(s).multi_line(true).build().ok())
                    .unwrap_or(HEADER_REGEX.clone());
            
            Edit: I would also consider this to be a diagnostics bug, for at least the first case rustc should have suggested .as_ref(). For the second part, the compiler would ideally have pointed at the `&s[..]` as part of the problem, and the sibling comment has the change you likely want.

            Edit 2: to further drive the point home that this should be a bug, this is the current output for a similar case while I was trying to minimize this:

                error[E0308]: mismatched types
                  --> src/main.rs:10:22
                   |
                10 |         .map(|s| bar(s));
                   |          ---         ^ expected `&Struct`, found struct `Struct`
                   |          |
                   |          help: consider using `as_ref` instead: `as_ref().map`
            
            https://play.rust-lang.org/?version=stable&mode=debug&editio...
            • devit 5 years ago

              The latter change introduces an unnecessary clone(). The correct approach is to instead introduce a variable so that headerRegex can keep being a reference to either that variable or to HEADER_REGEX.

              • ahelwer 5 years ago

                Can you expand on how this stops a clone from happening?

                • devit 5 years ago

                  If you don't call clone() then a clone doesn't happen...

                  • ahelwer 5 years ago

                    Ah, I didn't see the extra clone. I thought it was something to do with that additional variable you defined. I guess you need to do that or else you have to do a clone.

            • shepmaster 5 years ago

              > into an `Option<&str>` from an `Option<String>`

              `Option::deref` is a good choice here.

        • loa_in_ 5 years ago

          I have to agree that at the beginning those unfortunate names (Arc, Cow) confused me more than they should, given that I know perfectly well what Copy on Write and reference count means.

          If only it was spelled CoW and ARC I would very quickly grasp it (probably).

          But it's an extremely minor inconvenience and arguably makes the whole thing very memorable

      • devit 5 years ago

        1. You mean Cow<'a, str>? It's an enum of either String or &'a str, typically used with 'static so you can either store an heap-allocated string or a static string without copying it to the heap

        2. Given you mention "maps of option", probably you are doing something like opt.map(|x| &x.field) or equivalent which is obviously invalid (since you are returning a reference to a subobject of a function argument). Instead, you need to do opt.as_ref().map(|x| &x.field) (or as_mut() if it's mutable).

      • sanxiyn 5 years ago

        Clone more.

  • modeless 5 years ago

    I had a similar experience. It really made me question whether the borrow checker is worth it. I'm all for memory safety, but there are some pretty compelling low pause GCs out there now that can give you memory safety without the ergonomic problems.

  • iudqnolq 5 years ago

    I'm in a similar position. I highly recommend posting snippets of what you're trying to do in the Rust discord. The people are super nice and very helpful.

    Honestly the friendliness of the community is a big part of why I'm learning Rust. I found it so much harder to get free help from volunteers with c code issues. Not that I deserve it or anything, but it's really nice and I try to contribute back by writing PRs to improve the docs once I understand something.

  • Jweb_Guru 5 years ago

    As someone who's been there, I strongly recommend asking for help (maybe posting this on HN counts?). Bashing your head against the wall against Rust without some correct code to look at is not (IMO) a great way to learn the language... I definitely get the appeal, as someone who hates asking for help, but it's just not worth it in this case, especially when you're starting out and aren't even sure what terms to look for.

  • brundolf 5 years ago

    Short suggestion: If performance isn't critical, sprinkle some .clone()s in there on your strings and the borrow-checker will get off your back

    Long suggestion: Working with strings in Rust requires that you have a C++ mental model of what strings are and what properties (in the abstract sense of the word) they have. Rust will make sure you can't misuse them, and it will make them as ergonomic as they can be within that C++-like model, but it's a fundamentally different concept from what you have in languages like C# and Java, and if you're trying to look at things from that other perspective, you're going to keep running into walls over and over.

    More broadly, when you're using Rust you need to be in the basic mindset of a C++ programmer. From there Rust will make things significantly easier in many ways, but if you don't start with the right mental model, you're going to have a bad time. Strings are probably the most striking case of this because the way they get treated in virtually all higher-level languages is so wildly different from the way they get treated at the low-level.

  • cbHXBY1D 5 years ago

    This is my experience with the language. It's a good language - but it's a tool at the end of the day. One with edges and trade offs like any other. I can't help but think the people like the author only feel "giddy" because its been hyped up on this website.

  • devit 5 years ago

    That seems rather trivial, and I'm not sure how you could possibly have any issues with the borrow checker.

    Just do the first regex search, then escape into a String with regex::escape, then build a regex from the String and search with it.

    Of course what you are doing is probably wrong since you should use a string matching crate rather than escaping characters and using a regex matching crate.

    • ahelwer 5 years ago

      Since it's so trivial, I would be quite happy for you to implement the feature for me! https://github.com/tree-sitter/tree-sitter/issues/982

      Just lol at the idea that you won't run into borrow checker issues when dealing with strings though.

      • burntsushi 5 years ago

        Solving that issue isn't trivial. I just read it and I wouldn't know where to begin, probably because I don't understand the requirements.

        I think what's being called "trivial" is doing a bit of regex searching. It's probably accurate to call that trivial for an experienced Rust programmer, but if you're just beginning, I don't think it's helpful to call anything trivial. I still remember my first exposure to Rust. It was different. It took a bit to grok. But once it clicked, things were much better.

        As the maintainer of the regex crate, I invite you or anyone to ask for help using regexes. The regex repo has Discussions opened up, so it's appropriate to ask for help, even if they are beginner questions: https://github.com/rust-lang/regex/discussions

        As usual though, try to provide as many details as you can. Giving the source code you have but can't get to work is a great start, for example.

        • estebank 5 years ago

          Looking at their current iteration of the code, the problem is not in the regex crate's API, but rather on them not having the ownership system internalized yet.

          https://doc.rust-lang.org/rust-by-example/ and https://github.com/rust-lang/rustlings might be good places to visit to gain that experience, as well as improving the specific diagnostics to be more actionable.

          • burntsushi 5 years ago

            > the problem is not in the regex crate's API, but rather on them not having the ownership system internalized yet

            Yes, I figured that to be the case, but still wanted to say, "Discussions are open for questions, even if they are beginner related, as long as it's related to regex'ing somehow." :-)

      • organian 5 years ago

        I'm not the person you replied to, but here's some Playground code I just wrote that might be helpful for you, based on the issue you linked:

        https://play.rust-lang.org/?version=stable&mode=debug&editio...

      • iudqnolq 5 years ago

        I wrote code recently to parse a file with regexes and generate rust code from it. It's get-the-job-done code, not super clean, but you might find it useful.

        https://github.com/danielzfranklin/drm-fourcc-rs/blob/main/b...

      • zRedShift 5 years ago

        Maybe this can help you, I looked at your fork of tree-sitter to figure out what you were trying to do.

        https://play.rust-lang.org/?version=stable&edition=2018&gist...

        But what you really want is to get your head around ownership and borrowing, especially when it comes to the scoped RAII that rust has (e.g. freeing a String while a reference to it still exists and is being used would give you the "temporary value dropped while borrowed" error).

  • nemo44x 5 years ago

    Yes, lots of up front things that pay off after your code base gets larger and/or you ship.

yagizdegirmenci 5 years ago

> Rust Makes You a Better Overall Programmer

I agree with that, but this applied to nearly any language for me. I started doing Python back when 2013, then i learned Go, i started writing better Python code, then i learned C, i started writing better Go code, then i learned Rust, i started writing better code in general. The more you play with other language the more you learn other programming paradigms, it changes the way you thinking about programming completely.

  • Koshkin 5 years ago

    Well, it's like saying that wearing a straightjacket makes you a better person.

    • runarberg 5 years ago

      A while back there was an interesting post about dry stone walls[1] (e.g. stone walls erected without any mortar). The top comment included this recommendation for beginners in stone masonry:

      > When you pick up a stone from the pile, it MUST be placed on the wall. You either have to make it fit your intended spot through rotation or another adjustment, or you have to find another place on the wall for it. It CANNOT be placed back on the pile.[2]

      The post explains that using this restriction makes you a better masonry overall because you train your self to the exact skills required. I find this a lot better analogy to restrictive programming languages then a straitjacket. When you restrict your self to always place a stone you pick up, you train your eyes to first evaluate which kind of stones you need next, and you train your self to find that stone in a pile. I don’t see exactly what you are training exactly when you wear a straitjacket.

      1: https://news.ycombinator.com/item?id=20455860

      2: https://news.ycombinator.com/item?id=20469600

    • kobebrookskC3 5 years ago

      Just like a straitjacket stops you from hurting others, Rust stops you from writing memory unsafe code.

    • kazinator 5 years ago

      straitjacket: related to the word strait.

judofyr 5 years ago

This is a nice summary of the good features of Rust, but I'm failing to see how any of these points are in any way special for a "professional programmer". Doesn't a hobby programmer care about all of these points as well?

In my experience, whether you're getting paid or not correlates quite poorly with the requirements of the project. I've worked on professional code bases where bugs were totally acceptable ("just push a fix when the error comes in"), and I've done open-source projects where I definitely don't want anything to break ("I'd hate to release a broken version").

> The statement Rust is for Professionals does not imply any logical variant thereof. e.g. I am not implying Rust is not for non-professionals. Rather, the subject/thesis merely defines the audience I want to speak to: people who spend a lot of time authoring, maintaining, and supporting software and are invested in its longer-term outcomes.

Well, it looks like you're implying some logical variants, like "Rust is a better language than language X for professionals" or "Language Y is _not_ a language for professionals". I mean, "Language X is for Professionals" is true (by observation) for every single language out there being used professionally.

pizza234 5 years ago

While I'm a big fan of Rust myself, there are a few points that lack rigour in this article (which are typical of "I love X" articles). Three points that struck me:

- rust-analyzer is good (as in: it's functional, it's advancing, etc.etc.), but it's alpha; it's not a tool that can be considered a mature tool of a mature language. I often encounter issues while working on projects.

- learning Rust is a serious problem IMHO, not only because it's hard in itself, but it's because it's hard to structure a plan to learn it. listing the reference book is indeed a misrepresentation: a complete beginner that reads the whole book will still have significant problems in working on a real project; it's very unclear which step to take after reading it, as real-world Rust programming needs exercise which is not tackled by any book. Things are made worse by a number of garbage books (Packt being very guilty of this) that pretend to teach programming in Rust by slapping a 20-pages chapter on the syntax and basic concepts.

- "for the vast majority of code I author, Rust feels more like Python than C": feelings are subjective, so I can't argue in an absolute sense, but the complexity of programming is very, very far from Python. In scripting languages like Python/Ruby one doesn't need to care about anything: memory allocation (which in itself, has many consequences, including on the program structure), data types, syntactic rigour, exact consistency of the program (in the sense: one can develop a half broken Python app, and it will stull run); all of these things are required in a statically typed language. Golang is probably a language that is closer to Python than C.

  • skohan 5 years ago

    > learning Rust is a serious problem IMHO, not only because it's hard in itself, but it's because it's hard to structure a plan to learn it.

    I totally agree. One of the issues is that there are so many complex and novel topics that you will run into in your first week of working on a real project, and you have to wrap your head around all of them to some degree to be able to progress. If there is an obvious and clear progression path, I did not discover it.

    > the complexity of programming is very, very far from Python

    I could not agree more. These languages are deeply philosophically different in my mind.

colesantiago 5 years ago

TL;DR?

That looks like a very long essay / dissertation just to say 'I love rust'.

  • jvanderbot 5 years ago

    It is. It's not going to replace Python, and if maintenance is the metric, then "rewrite it in rust" is not the right move. But I do think Rust will gain traction, be a round a long time, and slowly become a complement to existing C/C++ ecosystems through wrapping and binding.

    I'm just not sure the other way will materialize. Until Rust can be used to feed back into C/C++ ecosystems and long-standing projects (like the linux kernel), it'll struggle to be the primary ecosystem / language for non-new (e.g., non-trivial) projects.

    • otabdeveloper4 5 years ago

      At this point Rust is not the prmary ecosystem for anything.

      The important and exciting new things are written in C++, not Rust. Rust has no advantages over C++ once you know C++.

      • PartiallyTyped 5 years ago

        I find a lot more things that I like in rust over Cxx; I like the trait system over Cxx's whole virtual functions and interfaces. I like algebraic types and forced enumeration of cases, I like immutable by default without the need to write const everywhere, and I certainly prefer cargo over anything I have seen in Cxx ecosystem.

      • jvanderbot 5 years ago

        I agree that Rust isn't the primary ecosystem. But that's not a reason to dismiss Rust. If Rust could create components that can be incorporated into C/C++ systems, then we'd have a real contender for systems programming. The problem is Rust is strict, and it only goes one way, from C/C++ to Rust. That's the right first step!

        Saying C++ is better than Rust "once you know it" doesn't make a lot of sense to me. No language is intrinsically better once mastered by a productive programmer.

      • daypay 5 years ago

        Isn't this what the cxx project is aiming to do? https://github.com/dtolnay/cxx

      • neonological 5 years ago

        This statement is wrong. Rust has many advantages over C++. Knowing c++ doesn’t make these advantages disappear. It’s like saying you know assembly language and can write 100% bug free code in assembly so C++ has no advantage over it.

        Realistically, both languages have advantages and disadvantages.

      • mlindner 5 years ago

        I would counter then that it's impossible to ever "know C++" then given that even C++ experts write in memory-based security exploits from time to time.

  • jmull 5 years ago

    > a very long essay / dissertation just to say 'I love rust'.

    I think you nailed it.

  • ModernMech 5 years ago

    After 14k words I think I've come to the TL;DR at the end: "I am irrationally effusive about Rust."

  • _6pvr 5 years ago

    TL;DR: Author likes Rust. Rust exposed them to new things. The title is totally misleading and irrelevant.

mnd999 5 years ago

I skimmed it, but I think what the author is getting at is that rust demands rigour. The kind of rigour required when failure is a problem and this tends to be the kind of code people get paid to write.

I guess the reverse is also true, if you’re writing the kind of code where failure doesn’t matter (e.g. spikes, experimentation, just messing about) perhaps rust isn’t the best choice.

  • skohan 5 years ago

    I think it's a little bit of a trap to fall into to believe that since Rust:

    1. provides some unique benefits in terms of safety, and

    2. is hard to program and requires rigor

    that all that rigor is "worth it", and that it makes you a better programmer to put up with it.

    Don't get me wrong, Rust's tradeoffs are valuable for some use-cases, but there are many, many use-cases where a GC'd language will work just fine, and it doesn't make you less professional for choosing a higher level tool which you can be more productive in if you don't have performance or memory constraints.

    I also think, as the author alludes to, that many programmers get their first exposure to algebraic types and the elimination of NPE's through Rust, and get the false impression that the benefits of these features are somehow related to the additional complexity required by Rust. But these features are not related. Languages like Swift have shown us that you can get many of the benefits of Rust in terms of providing an "if it compiles it works" experience without many of the challenges Rust imposes on the programmer.

    • mnd999 5 years ago

      Completely, I don’t see rust as a replacement for Scala, Java, Go (and especially) Javascript which tend to be what I use at work. It’s certainly possible, but I don’t think it would add much.

  • msla 5 years ago

    > I skimmed it, but I think what the author is getting at is that rust demands rigour.

    C demands rigor, too, but we're not talking about C because C compilers don't hold your hand and show you where your rigor slipped. Which is a sign C demands more rigor than Rust, isn't it? It's a problem with the author's argument: It's better to say that both C and Rust demand rigor, because they both compile to low-overhead executables (lower-overhead than C++, certainly), but Rust has more handrails and warning signs than C, so it's less dangerous.

  • rhn_mk1 5 years ago

    Perhaps there are better choices than Rust when failure doesn't matter, but Rust is still good at some of those.

    As an example, the rigour of strong typing helps set some experiments on the right track.

noisy_boy 5 years ago

> You see practices cargo culted across the decades (like the 80 character terminal/line width and null-terminated strings, which can both be traced back to Hollerith punchcards from the late 19th century)

I limit my lines to 80 chars for more practical reasons. I have a wide-screen 43" monitor and restricting content to 80 columns allows me to have the project/navigation pane + 4 vertical splits side by side. Couple of those vertical splits can be split horizontally and I can see/edit, say, 6 open files at once without any switching of windows. Probably sounds like overkill but once you experience it, you know its worth it. Plus my eyes like less horizontal scanning too.

  • jmull 5 years ago

    I'm not sure the author knows exactly what cargo cult means.

    It seems like the perpetuation of the use of null-terminated strings is more about interoperability with what came before.

    And like you suggest, 80 characters is at the limit of what people generally find comfortable reading. (I rely on editors to word-wrap code on-the-fly rather than insert line breaks manually... maybe that's what was meant? That's a tradeoff, though, since some tools don't do word-wrapping or don't do it well.)

hderms 5 years ago

Anyone who has a bunch of rust experience: does writing things like mutable tries, or other data structures with complex ownership semantics ever become truly natural? I find rust really straightforward to write when the ownership is straightforward, but I find myself out of my depth in a lot of cases.

snake_case 5 years ago

A few years back I chose Rust as the language I was going to build side projects with. I started out building a wasm A* pathfinding demo [1] to get a better hang of it.

A little bit later I wrote a CLI task runner [2] which is defined by a simple markdown file. I find Rust to be perfectly aligned with the goals of a CLI utility: single deployable binary and very low startup cost.

Most recently I launched a side project [3] (a jigsaw puzzle website) using Rust as my backend API service. I've been slowly building up a server framework over the years and finally was able to put it to use! Yes, it took me much longer to ship something in Rust versus other languages I'm more familiar with. But after learning Rust for a few years now, it doesn't take me much more time to build a feature than it would in another language.

Early on, I ran into a lot of borrower issues and got stuck many times. But after I got over those problems, I realized that for any future hurdles I would face, I just needed to keep pushing and eventually I would find a solution. I have found that with game development or heavily stateful apps, I tend to run into borrower issues more often. But for an API service with a simple input and output, I almost never run into borrower issues.

[1]: https://github.com/jakedeichert/wasm-astar

[2]: https://github.com/jakedeichert/mask

[3]: https://puzzlepanda.com

f154hfds 5 years ago

I have to concur with the author on Rust. I remember when I read the book a few years ago I had just spent several years doing professional development in C++ and Python, and had recently discovered the GIL much to my dismay. After reading the Book I felt like I stepped through some proverbial door and could never go back. The features by themselves aren't really that remarkable but all of them together in one package - every time I use Python or C++ now I want Rust syntax, tooling, etc.

Now, my biggest critique - because of Rust's emphasis on static dispatch and monomorphization (good decisions all around if you ask me), plus the fact that lifetimes provide their own type dimension - I find that open source projects can have absolutely monstrous types, impossible to reason about. It's tough because I prefer the WYSIWYG templating Rust offers over C++ duck typing any day but many of these crates' types are too complex. In fairness, most of the most egregious cases were due to the lack of const generics and those cases are quickly improving.

As an example, I've been playing around with websockets recently and ran into this type: https://docs.rs/websocket/0.26.2/websocket/server/upgrade/st...

Note 4 different impls, each with its own generic requirements! I'm sure each case makes sense somehow, but it sure complicates my life when I want to use a function and find it isn't implemented for _my_ WsUpgrade.

lmilcin 5 years ago

> The statement Rust is for Professionals does not imply any logical variant thereof. e.g. I am not implying Rust is not for non-professionals. Rather, the subject/thesis merely defines the audience I want to speak to: people who spend a lot of time authoring, maintaining, and supporting software and are invested in its longer-term outcomes.

I think the title is a little bit misleading and paired with the first sentence invites flame, but this one in the middle of article sums it up much better.

With some languages you pay some special costs that are only recovered for certain types of applications.

For example, programming in C is slow, tedious and bug-prone (as compared to Python) but it is easier to solve some types of problems (like writing system software, controlling memory layout for performance, conserve resources, etc.) For most projects the cost may be too high but if you are one of certain types of projects the pros will outweigh the cons.

In general, when programming with strong types you pay for long term maintainability (ability to automatically ascertain correct type of object at any point and extra features that come from it).

In Rust you pay even more for even more benefit in controlling the types and ownership of the data which means this environment should be thought as geared even more towards long term maintainability.

valand 5 years ago

Even learning Rust on the surface already change a lot on how you perceive errors, type-based error, thread-safety, expression-oriented code and lifetime.

I write typescript for work. I have adapted some concepts such as the encoding invariants into type (using a proven library such as fp-ts and io-ts) and using return over throw for error handling has been yielding better and scalable code.

Not to mention Rust's types for managing concurrency and shared objects, like Rc, Arc, Mutex, that drive the architecture of your software. I have been doing a similar thing in my projects in other language and the impact has been very positive

Edit: typo

pantulis 5 years ago

"(...) Rust feels more like Python than C. (...)"

This feels like a very powerful assertion. Does the rest of the HN audience agree with this? If this is true, how long did it take?

  • rhn_mk1 5 years ago

    For me, this feeling comes from how Rust APIs are typically built. They embed enough information about allowed usage that shooting yourself in the foot is not trivially possible (unlike C).

    I guess one could say APIs expose functionality on the level somewhat similar to C++, with builtin strings, vectors, and other high level data structures that are namespaced and are also objects. Whether that is more C-like or Python-like is for the reader to answer.

  • skohan 5 years ago

    I'm a bit confused by this take. With Rust you're constantly thinking low level details, like which type of reference something is. I would place it closer to C than Python, but closer to C++ than either.

    • estebank 5 years ago

      I wouldn't say that thinking about ownership is "low level details". You are (or should?) always having to think about this when building APIs. The difference is that in for example Python you don't have a way to encode that information and enforce it. I would say that the "feeling" of writing on a high-level language comes from a few ergonomic features:

      - Match ergonomics, so that you have to think about borrowing less in patterns

      - The compiler providing suggestions for those cases where you must specify them

      - Type inference

      - Iterators letting you write fairly functional code

      Things like having to write &*foo[..] or .as_mut_ref() are indeed "warts" when you don't care about those details, but they happen uncommonly enough that it isn't perceived as an onerous cost.

      • skohan 5 years ago

        > You are (or should?) always having to think about [ownership] when building APIs.

        This isn't strictly true. For instance Swift uses value semantics, which is equivalent to adding an implicit `.copy()` in rust every time most variables are passed around. It obviates a lot of the low-level details, at the cost of performance and control. This is a lot more "python-like" in my opinion, where having to hold these details in your mind is very much against the design priorities of python.

        Python wants everything to be implicit, to eliminate tedium wherever possible, and for syntax to barely exist. By contrast Rust favors explicitness, demands a lot ceremony, and is very syntax-heavy. These languages are antithetical in so many ways.

        It's fairly subjective, so I would not try to convince you that Rust is more similar to C than Python, but many of the points you brought up apply to virtually every modern mainstream language. It's hard to imagine why Python would come up as a comparison point for Rust unless C and Python were literally the only other languages you had ever programmed with.

  • steveklabnik 5 years ago

    It really depends on what those languages mean to you. I don't think that you can answer these sorts of questions in the general case.

    You can sorta kinda compare programs though. I had a discussion about this on HN last year. Note that this is an extremely small sample size, but you know https://news.ycombinator.com/item?id=22712441

    I wrote an updated version half a year ago https://news.ycombinator.com/item?id=24595081

    YMMV. It depends on a lot of factors.

zabzonk 5 years ago

Could equally well say "C++ is for professionals", or "Java is for professionals" and so on ad nauseam, with pretty much the same specious justifications.

ocschwar 5 years ago

The title implied that it's not for amateurs, and now that I have some data about Rust and teenagers, I can say it very much IS for teenagers. After one afternoon wrestling with GDB and C code, they're ready to put up wrestling with the borrow checker.

  • CodeIsTheEnd 5 years ago

    While in many cases that may be the implication, the author clearly states that is not their intention in the first few paragraphs:

    > The statement Rust is for Professionals does not imply any logical variant thereof. e.g. I am not implying Rust is not for non-professionals.

Koshkin 5 years ago

Depends on the meaning of the word 'professionals.' Most of those who earn their living by coding (which is whom I would call professionals), use JavaScript, C++, Java, or C# - and, I'm afraid, will continue to do so for many years to come.

osigurdson 5 years ago

Rust is for the true Scotsman.

geodel 5 years ago

The way I see is Rust for people who love to write about writing code quite a bit more than writing code itself.

mastrsushi 5 years ago

Rust sounds like a pretty promising language. Considering in the future everything will run in garbage collected VMs written in C++ anyway ;)

StreamBright 5 years ago

> To me, Rust introduced a number of new concepts, like match for control flow, enums as algebraic types,

He must be joking.

  • masklinn 5 years ago

    The author specifically and explicitly pointed out 3 times in 2 paragraph that these were new to them, not to the world at large. In fact, they spelled this out very explicitely in the paragraph following the one you selectively quoted:

    > Many of the new concepts weren't novel to Rust. But considering I've had exposure to many popular programming languages, the fact many were new to me means these aren't common features in mainstream languages.

    And… they're right. Other languages cropped up with overlap around the same time (mostly Swift), but sum types, pattern matching, option types, … were not common features, and borrow checking remains rather unique.

    • StreamBright 5 years ago

      He also claimed that he programmed in Erlang. How can you program in Erlang without pattern matching?

  • VBprogrammer 5 years ago

    I think you might be misinterpreting that as "Rust introduced these things" rather than "I learned about these things from Rust".

  • danpalmer 5 years ago

    The author does mention a Haskell/Scala like language missing from their list of languages they have written, but then they do include Erlang which I believe has pattern matching as a fairly core construct? This does lead me to doubt how much Erlang they've written, and in turn how much they've written across all of the languages mentioned.

    • StreamBright 5 years ago

      Exactly. How can somebody claim to program in Erlang and also claim that Rust introduced pattern matching to them?

      Erlang has the best pattern matching abilities and it is all around in every single Erlang code I have ever seen.

  • oytis 5 years ago

    He means Rust introduced these concepts to him, not in general.

  • TheAdamAndChe 5 years ago

    When rephrased as "Rust introduced to me a number of new concepts, like match for control flow, enums as algebraic types," it's much more clear what he means. We all learn somehow.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection