[HN Gopher] Understanding Rust futures by going way too deep
       ___________________________________________________________________
        
       Understanding Rust futures by going way too deep
        
       Author : tempodox
       Score  : 428 points
       Date   : 2021-07-26 06:26 UTC (16 hours ago)
        
 (HTM) web link (fasterthanli.me)
 (TXT) w3m dump (fasterthanli.me)
        
       | the_duke wrote:
       | For an excellent, in depth introduction to async in Rust I
       | recommend this post [1], part of the "Writing an OS in Rust"
       | series.
       | 
       | This seems to be more of a hands on guide for the ecosystem.
       | 
       | [1] https://os.phil-opp.com/async-await/
        
         | tikkabhuna wrote:
         | Another hands on guide I really rate is Tokio's [1].
         | 
         | You create a very minimal Redis clone progressively by swapping
         | out synchronous for async.
         | 
         | [1] https://tokio.rs/tokio/tutorial
        
       | beltsazar wrote:
       | I've read many articles explaining Rust async and I think the
       | best one is the async chapter of Programming Rust (2nd edition).
       | It's not too daunting for someone new to async concepts, and yet
       | comprehensive enough for someone curious about how Rust async
       | works (future, waker, pinning).
       | 
       | Amazon link: https://www.amazon.com/dp/1492052590
       | 
       | Direct link to the chapter (if you subscribe to O'Reilly/Safari):
       | https://learning.oreilly.com/library/view/programming-rust-2...
        
       | Walther wrote:
       | Thank you once again for writing such an excellent article <3
       | 
       | In contrast to some other comments here, I love the narrative
       | style. After all, these are half tutorials half entertainment -
       | and the entertainment keeps it enticing. I would not have spent
       | nearly two hours reading about the nitty-gritty of futures
       | implementation details today if it weren't for the way you write
       | about things.
       | 
       | To put it the other way - if I wanted to read a super condensed,
       | concise reference of how these things work, I would, you know,
       | read a reference. The narrative style of building things on your
       | own, solving problems as you face them, picking tools and
       | libraries on the way, feels relatable as the everyday lived
       | experience of software development. It feels appropriate for the
       | medium - it's a blog after all!
       | 
       | Besides, blogs are the perfect place to mention the tiny useful
       | tools and libraries people might not have heard of. References
       | and books might err on the side of being "neutral" and not making
       | opinionated recommendations, but this can also lead to unknown
       | unknowns. People don't know what they are missing out on!
       | Luckily, blogs have more freedom here. Mentioning things like
       | `cargo-edit`, `serde` and others can probably make some readers
       | feel like "one of today's lucky ten thousand" - enjoying the
       | newly-gained quality of life improvements.
        
         | ordu wrote:
         | Yeah! This article not just explains how async works, the
         | author show research methods to learn how async works. How
         | something works is transient knowledge, how to learn how
         | something works is timeless priceless knowledge.
         | 
         | The methods include strategically inserted panic!, some nice
         | crates with examples how to use them... I cannot say I've
         | learnt a lot about async, but I got few insights on
         | technicalities of self-learning.
        
         | [deleted]
        
         | shantanugoel wrote:
         | +1 I enjoyed the writing style thoroughly. It made
         | understanding the concepts super simple and fun to go through.
        
       | MrBuddyCasino wrote:
       | _Gaze upon our work, and rejoice:_                       pub fn
       | try_join<A, B, AR, BR, E>(a: A, b: B) -> impl Future<Output =
       | Result<(AR, BR), E>>         where             A: Future<Output =
       | Result<AR, E>>,             B: Future<Output = Result<BR, E>>
       | 
       | I get why its like that, but I really wished this wasn't a
       | typical Rust generics signature.
        
         | magicalhippo wrote:
         | I don't know any Rust, and that thing made perfect sense to me
         | (compared to other things in Rust).
         | 
         | How would you have preferred it looked?
        
         | jcelerier wrote:
         | Generics are about encoding supplemental bits of information in
         | the type system. That information has to be spelled out
         | _somewhere_
        
           | cormacrelf wrote:
           | Yes, however in my experience you typically have to write out
           | constraints like these a bunch of times. Refactoring a
           | generic trait with 10 implementations is a nightmare. A
           | struct with a couple of variations on the constraints for its
           | impl blocks is also painful beyond like one generic param. It
           | has to be spelled out somewhere, but then at minimum
           | somewhere else, and in general about 3-4 more times.
           | 
           | I see why Haskell folks like their type level functions.
        
             | tinco wrote:
             | In Haskell I used to do this trick where if I wanted to
             | neatly type annotate, but my function had some crazy
             | signature, I'd compile the program, but passing the
             | function into another function in an intentional incorrect
             | way. The compiler would complain "expected A but got B".
             | And I would simply copy B and make it the type annotation
             | :P
             | 
             | Nowadays the editor would help you do that. When I'm coding
             | Rust the editor shows the types inline in a small font,
             | which is very helpful.
        
             | myrrlyn wrote:
             | yeah, bounds written on the type decl _shouldn 't_ be
             | repeated on the impl. the current typeck is not smart
             | enough to do that but the ever-"nearly there we promise"
             | replacement is. it'll land one day. they promise
        
         | Ygg2 wrote:
         | Do you have a better way to write the same constraints?
         | 
         | Conceivably if there was a way to not repeat the future parts.
        
         | drran wrote:
         | It's easy to read, isn't?
         | 
         | pub fn - public function
         | 
         | <...> - declaration of types,
         | 
         | A - type of first future,
         | 
         | B - type of second future,
         | 
         | AR - type of result of first future
         | 
         | BR - type of result of second future
         | 
         | E - type of error
         | 
         | So, public function try_join accepts two futures, which my
         | return <AR>esult and <BR>esult or <E>rror, and returns future
         | which will return tupple with (<AR>, <BR>) or <E>rror.
        
           | scoutt wrote:
           | > It's easy to read, isn't?                   pub fn
           | try_join<A, B, AR, BR, E>(a: A, b: B) -> impl Future<Output =
           | Result<(AR, BR), E>>         where             A:
           | Future<Output = Result<AR, E>>,             B: Future<Output
           | = Result<BR, E>>,
           | 
           | Come on... "A", "B", "E", "a", "b"??? Naming stuff like this
           | is the standard way? I mean, if I know nothing about what the
           | code does, and I find this kind of function, I'll start
           | swearing right away.
           | 
           | Also, the function only does:
           | TryJoin::Polling {             a: State::Future(a),
           | b: State::Future(b),         }
           | 
           | Which is clearer and shorter. Why even writing a function
           | signature like that? It's longer and more complicated to
           | understand than the code itself! I don't know much Rust, but
           | since a semicolon is missing this is just a return value,
           | right?
           | 
           | So why not?                   let res = TryJoin::Polling {
           | a: State::Future(fetch_thing("first")),             b:
           | State::Future(fetch_thing("second")),         }.await?
           | 
           | PS: I don't want to criticize, and I'm not a Rust literate,
           | I'm just amazed with its complexity.
        
             | gpderetta wrote:
             | To be fair the full expansion of A and B are literally on
             | line below.
        
             | kmeisthax wrote:
             | So, there's a few things to point out here:
             | 
             | - The example you gave at the bottom won't really work,
             | because enum variants aren't fully-fledged types on their
             | own and they can't impl `Future`. Therefore, you can't
             | `.await` them on their own. Furthermore, the point of
             | `try_join` is to poll _both_ futures until one fails, which
             | you can 't do with `async` syntax.
             | 
             | - Rust requires that you spell out all generic parameters
             | like this at function boundaries. It's technically possible
             | to infer types across such boundaries, but that has the
             | potential for significant changes to the typing of a
             | function to go entirely unnoticed. In fact, I believe some
             | of Rust's documentation specifically calls out problems
             | Haskell had with inferring function signatures like this.
             | 
             | That being said, you are correct that we could do this
             | instead:
             | 
             | ```pub fn try_join<FutureA, FutureB, ValueA, ValueB,
             | Error>(future_a: FutureA, future_b: FutureB> -> impl
             | Future<Output = Result<(ValueA, ValueB), Error> where
             | FutureA: Future<Output = Result<ValueA, Error>>, FutureB:
             | Future<Output = Result<ValueB, Error>>,```
             | 
             | That _is_ slightly more readable.
        
               | greenshackle2 wrote:
               | I'm used to the convention of giving short all-caps
               | acronym names to type parameters, so this is less
               | readable to me, because I automatically assume that
               | something named `Error` is a type, not a type parameter.
        
               | scoutt wrote:
               | Oh god. I guess Rust designers have not foresee a
               | function taking 5 parameters.
               | 
               | At this point, somehow, I'm starting to consider C++
               | templates beautiful! Perhaps a signature form like:
               | template <FutureA, FutureB, ValueA, ValueB, Error>
               | pub fn try_join() ... etc.
               | 
               | would be clearer at this point.
        
         | paavohtl wrote:
         | I don't think this is a _typical_ signature. On the contrary,
         | it's quite exceptional. In my experience having more than 1
         | type parameter in a function is quite rare in most code, unless
         | you're building an advanced, composable library like a futures
         | implementation.
        
           | MrBuddyCasino wrote:
           | I didn't mean "typical" as in "most Rust fn signatures are
           | like that", obviously most functions are far simpler. Who
           | would even claim that?
           | 
           | I meant "you will only encounter this level of generics
           | complexity in Rust" (disclaimer: I don't know C++). They
           | can't be that rare either, I have not written a lot of Rust
           | but already encountered similar constructs multiple times.
           | But admittedly that might be partly due to no-std
           | requirements.
        
             | jen20 wrote:
             | C#'s `Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>` [1] would like a
             | word!
             | 
             | [1[: https://docs.microsoft.com/en-
             | us/dotnet/api/system.tuple-8?v...
        
         | richardwhiuk wrote:
         | You can do:                 pub fn try_join<A: Future<Output =
         | Result<AR, E>>, B: Future<Output = Result<BR, E>>, AR, BR,
         | E>(a: A, b: B) -> impl Future<Output = Result<(AR, BR), E>> {
        
           | diragon wrote:
           | Rust truly is a better C++.
        
       | zwirbl wrote:
       | Haven't read this article yet, but I've enjoyed most of the
       | previous ones from amos
        
       | masklinn wrote:
       | > I don't know! Why do they truncate stuff like that?
       | 
       | Because Linux limits thread names to 15 bytes (not including the
       | required terminal nul).
       | 
       | Other unices are better though not great, macOS is 63 I think.
       | 
       | By comparison Windows allows 32... kilobytes.
       | 
       | Though "runtime" is probably what should be shortened here, "rt"
       | is literally the name of the tokio features for configuring the
       | runtime, so that would likely be more understandable than the
       | trailing "-w".
        
       | ArchieMaclean wrote:
       | Based on my understanding from the article...
       | 
       | Iterators change over space, Futures change over time.
       | 
       | In iterator, every time you call 'next' you get a value until the
       | end when you get nothing. In Future, every time you call 'poll'
       | you get nothing until the end when you get a value.
        
       | DixieDev wrote:
       | Not related to async in Rust, but a 26 sec clean build time for a
       | project with 69 dependencies and no code of its own just to get
       | to the point where we can "do something useful" feels a little
       | wasteful.
        
         | pornel wrote:
         | Async runtime, channels, tracing, backtraces. It does compile a
         | lot of useful stuff ready for you to use.
         | 
         | It's all cached, so consider it more of a library install time.
        
         | kungito wrote:
         | I'd say it's pretty unfair to take clean build time to consider
         | doing something useful. 98% of my rust builds are incremental
         | builds which take 10-15 seconds for a project I have been
         | working on for 9 months full time and I think my dependency
         | tree has easily 1000 dependencies.
        
           | DixieDev wrote:
           | True, it's not a real problem in most scenarios if
           | incremental compile times are good. I still feel uneasy
           | depending on so many other crates, but this seems to be a
           | level of paranoia that others in the community don't share.
           | 
           | Having 1000 dependencies sounds crazy to me! If there's a bug
           | in even one of them that affects you then there's gonna be a
           | lot of digging to figure out the cause, and possibly multiple
           | pull requests to get it fixed. I think my CPU would spend a
           | bit longer than 10-15 secs on the linker step, too.
        
             | myrrlyn wrote:
             | the reason for
             | 
             | > I still feel uneasy depending on so many other crates,
             | but this seems to be a level of paranoia that others in the
             | community don't share. Having 1000 dependencies sounds
             | crazy to me! If there's a bug in even one of them that
             | affects you then there's gonna be a lot of digging to
             | figure out the cause
             | 
             | is that there is far, far more likely to be a bug in the
             | version you write on your own to achieve the same goal than
             | there is in a widely-observed library written by somebody
             | who's chosen to specialize in that specific thing
        
               | usefulcat wrote:
               | > there is far, far more likely to be a bug in the
               | version you write on your own
               | 
               | In general I agree with this, but there is another
               | relevant aspect to consider: something that I've written
               | on my own for a particular project is also likely to be
               | more purpose-built, and therefore simpler.
        
               | hnlmorg wrote:
               | In theory yes. But in practice that isn't always true.
               | People often don't audit other modules on the assumption
               | someone else had. Which means nobody ends up doing it.
               | And if you end up with an ecosystem that favours more
               | modules over fewer, you can end up with more modules than
               | a given developer or team are willing to audit (a bit
               | like "alarm fatigue" where if you have too many objects
               | to check then people will inevitably just get lazy).
               | 
               | Just look at how many C and C++ libraries are maintained
               | by 1 individual and have almost no 3rd party oversight to
               | see that Rust can't automatically make the claim you
               | made.
               | 
               | That all said, for anything complicated and/or directly
               | security related, one should always check if there is a
               | module first.
        
               | db48x wrote:
               | I look at it the other way around. You own any bug in
               | your product whether it comes from a dependency or from
               | code of your own; you have to fix the bug either way.
               | Using a dependency doesn't reduce your responsibility,
               | but it does reduce the amount of code that you have to
               | write yourself.
        
               | hnlmorg wrote:
               | But if you are willing to own that responsibility then
               | you should read the code you're importing to begin with.
               | I know I do but I also know most people don't bother.
               | 
               | I do acknowledge that there will always be bugs that are
               | identified by your users but equally if you're not
               | auditing your dependencies first then it's hard to argue
               | that you're not just passing off that responsibility
               | wholesale to your users.
        
               | kungito wrote:
               | It's always a tradeoff whether you want to read some
               | other code or work on something else. Rust ecosystem is
               | not that mature so for a few libraries I had to end up
               | rewriting the thing myself with some fixes or without
               | some bloat. I'm writing an application level thing and I
               | need as many utilities as possible as I do not want to
               | write all the layers for all the abstractions that end up
               | in my product. Then when something breaks I investigate,
               | offer a fix, open an issue or whatever. I'm not writing
               | something that requires too much reliability or whatever,
               | the utility is elsewhere.
        
             | volta83 wrote:
             | I rather have 1000 dependencies than re-implement all those
             | 1000 dependencies myself, chase all the bugs that have
             | already been ironed out in those, etc.
             | 
             | My time is worth more than my computer's time.
        
             | fasterthanlime wrote:
             | Compile/link times are definitely a discussion topic.
             | Here's some quick tips:                 * Incremental
             | compilation helps locally, not so much in CI, unless you
             | save/restore the whole cache which gets large quickly and
             | evicting the out-of-date objects is non-trivial.       * In
             | CI, sccache helps, but not as much as I'd like: there's a
             | bunch of things that are non-cacheable, notably crates that
             | pull in & compile C/C++ code (I'd trade 2 c-bindings crates
             | against 12 Rust-only crates any day for that reason alone)
             | * Splitting stuff across different crates helps
             | parallelizing the build (and caching it better), which may
             | be one of the reasons some projects end up having "1000
             | dependencies" (although I've rarely seen upwards of 600)
             | * For incremental builds, linking does become the
             | bottleneck. Full LTO is especially slow, Thin LTO is
             | better. Switching to LLD improves thing. I'm hopeful that
             | mold will improve things some more.       * Re multiple
             | PRs: thankfully crate families tend to live in the same
             | repository, so fixing something across both tracing /
             | tracing-subscriber could be a single PR, for example.
             | 
             | I wish the Rust community invested more in build caching,
             | but even with the current state of things, there's often
             | steps you can take to make things better.
        
               | styluss wrote:
               | Have you tried sccache?
               | https://github.com/mozilla/sccache
        
               | riquito wrote:
               | It's his second bullet point
        
       | jokethrowaway wrote:
       | This is a pretty great article but I don't get the hate for
       | futures not executing immediately and requiring an .await.
       | 
       | It's great to have pure futures-returning functions and being
       | able to isolate side effects easily.
       | 
       | I find it much easier to reason with (coming from mainly JS,
       | where everything is executed as soon as a promise is created).
        
       | the__alchemist wrote:
       | What are y'all's thoughts on Async in embedded? A portion of the
       | Rust embedded OSS community really likes it as an abstraction
       | over interrupts, critical sections etc. I've been burned by over-
       | complexity and coloring problems with it before, eg in web
       | programming.
       | 
       | Do you think it's a good fit? Use-case-dependent? Maybe for
       | things like RF?
        
         | steveklabnik wrote:
         | We don't need it, so we don't use it. I think it's fantastic
         | that it's possible, and I think that if you do need it, it's
         | great to have it.
        
         | floatboth wrote:
         | There is an executor project: https://github.com/embassy-
         | rs/embassy
         | 
         | I'm very excited about this for one simple stupid reason:
         | sleep(). Awaiting a timer delay deep inside some code is gonna
         | be _amazing_. With typical sync code, you basically have to
         | split your code and queue up the next work somewhere that would
         | be picked up by the timer interrupt... basically doing the
         | whole state-keeping that async would do for you.
        
           | the__alchemist wrote:
           | That sound like a nice advantage. I'd like to give this a
           | shot on a future project. Its creator and I share some API
           | design ideas, like using less typestate programming than
           | other conventions, but I haven't used Embassy due to being
           | cautious of Async. (The Typestate thing is a tangent; short
           | explanation is it lets you check for misconfigured pins etc
           | at compile time, but makes syntax significantly more verbose
           | and harder to refactor)
        
       | thrwyoilarticle wrote:
       | >When deciding which article to read during their coffee break,
       | people usually open several websites at the exact same moment,
       | and read whichever article loads first.
       | 
       | >And that's a fact. You can quote me on that because, well, who's
       | going to go and verify that? That sounds like a lot of work. Just
       | trust me on this.
       | 
       | Sure, but I click away when it hurts to read.
        
       | yodelshady wrote:
       | I am surprised Rust doesn't lean into the type system more for
       | declaring async, as I really like that way of framing it.
       | Admittedly, I'm probably not writing ```impl Future<Output =
       | Result<(), Report>> + 'a``` all that much in practice.
       | 
       | I like fasterthanlime's writeups but it does have one particular
       | bugbear for me, that seems _particularly_ common for Rust (and
       | Python, to be fair) feature writeups: environment-based yak
       | shaving because the author likes the mostly-unrelated library
       | <X>. The very first thing that happens if I try to reproduce this
       | code is a build error for cargo-edit, which is _totally unrelated
       | to the feature being taught_ , yet if I wanted to follow the
       | writeup, I'd have to fix it.
       | 
       | I do _really_ have this bugbear, particularly with Rust tutorials
        
         | ohazi wrote:
         | > I am surprised Rust doesn't lean into the type system more
         | for declaring async, as I really like that way of framing it.
         | Admittedly, I'm probably not writing ```impl Future<Output =
         | Result<(), Report>> + 'a``` all that much in practice.
         | 
         | Futures was stabilized before the async syntax, so this used to
         | be the _only_ way you could do it. But those were definitely
         | the bad old days.
         | 
         | A handful of (brave) developers and library writers wrote async
         | code like this back then when they really needed to, but the
         | vast majority waited for async syntax to stabilize. Those state
         | machines get ugly _real_ fast.
        
         | j1elo wrote:
         | I stand by my opinion that any kind of documentation or
         | tutorial should pass a validation step where the author does
         | copy/paste of all their commands into a clean Docker container
         | or VM image. If any error happens, your work is not done yet.
         | 
         | If you need to instruct users to install a compiler or an extra
         | tool, so be it. It just must work as-is on a clean system (for
         | which using Docker is easiest for me, but a clean VM or any
         | other alternative is fine too).
        
           | gmac wrote:
           | Preferably the author doesn't copy and paste: preferably
           | there's a documentation build step where everything gets run,
           | relevant outputs get inserted into the docs, and any errors
           | get caught.
           | 
           | This is how my docs for Zapatos[1] work, for instance. :)
           | 
           | [1] https://jawj.github.io/zapatos/
        
             | j1elo wrote:
             | It is probably too much to ask everyone follow the same
             | approach, so I'll be happy if people just test their own
             | commands by hand. But your doc build step that validates
             | code and catches errors? That's the ideal setup, for sure.
             | Great job!
        
           | chromatin wrote:
           | > I stand by my opinion that any kind of documentation or
           | tutorial should pass a validation step where the author does
           | copy/paste of all their commands into a clean Docker
           | container or VM image. If any error happens, your work is not
           | done yet.
           | 
           | Very much this. We often write scientific software with
           | (command-line) instructions for nontechnical or sort-of-
           | technical users and have learned from experience to not
           | assume anything about the user's environment, or background
           | knowledge, and to try to replicate our documentation's
           | instructions line-by-line!
        
         | richardwhiuk wrote:
         | This article takes ages to get to futures because the writer
         | seems to want to go on some tour of Rust crates first.
        
           | cormacrelf wrote:
           | IMO if your article is long enough that it crashes
           | MobileSafari, it should have a table of contents. Or it
           | should be a book chapter. It's a really obvious criticism of
           | all of fasterthanlime's posts that scarcely bears repeating
           | at this point but they're not getting any shorter. I get that
           | he has a lot to say but I just don't have time to wade
           | through all of that. What slice of Rust users are both such
           | noobs they haven't heard of cargo-edit and can't Google it,
           | but also so advanced they are interested in implementing
           | Future manually to deepen their understanding? It's ok to
           | teach both things but why together?!
        
             | myrrlyn wrote:
             | > What slice of Rust users are both such noobs they haven't
             | heard of cargo-edit and can't Google it, but also so
             | advanced they are interested in implementing Future
             | manually to deepen their understanding?
             | 
             | My colleagues. Many people are skilled programmers jumping
             | into obscure problems with only a baseline level of
             | familiarity with the greater environment.
        
               | Filligree wrote:
               | Or for example me.
               | 
               | cargo-edit looks useful. None of the tutorials I've
               | skimmed have mentioned it.
        
             | fasterthanlime wrote:
             | Ok I promise I'm going to stop replying to those comments
             | (because as you pointed out they show up on every post),
             | but! Rust is not the only language with a decent type
             | system out there.
             | 
             | I can definitely see some ML/Haskell/Scala/etc. folks who
             | would quickly pick up on all the Future stuff, drawing
             | parallels with their usual language of choice, yet being
             | completely unaware of all the nice tooling around Rust.
             | It's one of its main selling points for me!
             | 
             | Re cargo-edit specifically, it also lets me stay in flow
             | while writing these: instead of having to repeat "we'll
             | just edit Cargo.toml, make sure to have that under
             | [dependencies], uh this time we need a feature so the
             | right-hand-side needs to be an object, not a string" -
             | instead I can just copy into the article the actual
             | commands I run on my side, and if a quick paragraph about
             | where that subcommand comes from unblocks even one person
             | then it's worth it.
        
               | nindalf wrote:
               | I wouldn't worry too much about it. You have your style,
               | and it clearly works for the people who like your work.
               | Everyone likes different things, and it doesn't make
               | sense to force homogeneity. It's ok to cater to niches.
        
               | WJW wrote:
               | FWIW, I fall exactly into that niche. I've never written
               | a line of Rust in my life but am quite competent with
               | Haskell and Ruby and have dabbled in at least half a
               | dozen others. Seeing everyday `cargo` use made me think
               | of bundler and the Future/Result type signatures were
               | easy to map onto Async/Either and the like.
               | 
               | So yes, it's a long article but I didn't mind that in the
               | least. Keep it up!
        
           | fasterthanlime wrote:
           | Not to worry, we also spend some time messing with strace and
           | GDB. I see it more as "giving a working knowledge of Rust"
           | than a distraction. Some of these crates/tools are
           | lifechangers, I wouldn't want folks to miss out on them.
           | 
           | I'm sorry yodelshady has run into a build error for cargo-
           | edit (was it OpenSSL? screw OpenSSL) but also: the more folks
           | run into these issues and report them, the sooner they're
           | fixed. Also! I think cargo-edit should just be adopted by the
           | default cargo distribution because it's all-around really
           | good.
        
             | michael_j_ward wrote:
             | > we also spend some time messing with strace and GDB
             | 
             | Yes, and if I had learned about these a couple months ago
             | it would have saved me ~8 hours tracking a bug down. So,
             | thank you.
        
             | PudgePacket wrote:
             | I only learnt about the logging and tracing crates existed
             | from this blog post and I've already started using them, so
             | thank you!
        
               | hmfrh wrote:
               | The Zero to Rust[1] series also has a fairly
               | comprehensive look into these.
               | 
               | [1]: https://www.lpalmieri.com/posts/2020-09-27-zero-to-
               | productio...
        
             | steveklabnik wrote:
             | Tracking issue on cargo-edit here: https://github.com/rust-
             | lang/cargo/issues/5586
             | 
             | I too really, really want this. If only I had the energy to
             | work up some patches...
        
             | mikro2nd wrote:
             | I have to agree with the first comment: Speaking as someone
             | who was a tech trainer and writer of course materials,
             | tutorial, etc. for a couple of decades, if ``carge-edit``
             | is not core to what you're trying to teach, _leave it out_.
             | Get your own desires (ego) out of the way, and stick to
             | teaching what you need to teach. Leave the distractions
             | /side-tracks/proselytising to a separate lesson. You think
             | of the lesson as "Giving a Working Knowledge of Rust"?
             | Great! Then title it accordingly and don't dress it up in
             | "futures" camouflage -- you're doing your readers/learners
             | a disservice.
             | 
             | Many, _many_ tutorials make this mistake.
        
               | kbenson wrote:
               | > Then title it accordingly and don't dress it up in
               | "futures" camouflage -- you're doing your
               | readers/learners a disservice.
               | 
               | In their defense, the title is "Understanding Rust
               | futures _by going way too deep_ ", and I think advice and
               | tangents on best practices modules is easily covered by
               | "going way too deep".
               | 
               | > you're doing your readers/learners a disservice.
               | 
               | Not every tutorial needs to be the same, cater to the
               | same type of person, or try to explain things in the same
               | manner. I think the bigger disservice to readers/learners
               | would be to homogenize tutorials into what you think is
               | best. That might work best for a certain type of person
               | at a certain skill level, but that doesn't mean everyone
               | will be served well by that. And as the comments here
               | illustrate, it's not like there's a dearth of Rust
               | futures tutorials and explanations. There's definitely
               | room for a rambling, informal, irreverent, meandering
               | take (it's the only thing that kept me reading until the
               | end, or even past the first page or two).
        
               | jfrankamp wrote:
               | This isn't a tutorial, nor a training, nor a course
               | though, this is a cave exploration. Its going down the
               | wrong paths, multiple times on purpose. So pack your bag,
               | get your tools in order, we may not return. I really like
               | this style, the wrong paths/side tracks have so much to
               | teach us (more than the right paths).
        
               | actuallyalys wrote:
               | As someone who's been a technical writer (albeit for
               | years rather than decades), I think this is often good
               | advice, but not here. As other people have pointed out,
               | this isn't trying to be a Futures 101 or Futures for
               | Total Beginners. The title or opening could maybe be
               | clearer about its scope (although I think "way too deep"
               | at least implies the author isn't covering the topic as
               | directly or succinctly as possible), but I think the
               | digressions are interesting to Amos' target audience and
               | part of his style.
        
               | freeopinion wrote:
               | You have mistaken this for a tutorial.
        
             | SAI_Peregrinus wrote:
             | I'd say this is a good explanation, but a bad tutorial[1].
             | Which is fine, they're different things. But if you _want_
             | it to be a tutorial it should be focused on just Rust
             | Futures, and the other stuff should be separated out.
             | 
             | [1] https://documentation.divio.com/
        
         | Zababa wrote:
         | On the other hand, I really like that way of giving "best
         | practice" advice, as it's really useful advice. You end up with
         | a deeper understanding of futures, but also concrete advice
         | that you can apply easily.
        
       | DiabloD3 wrote:
       | Woah, he uses Iosevka for his mono font. :D
       | 
       | Edit: Whoever is downvoting, Iosevka is one of the favorites of
       | the HN crowd, and also my own daily driver. It is worth taking
       | for a spin if you haven't yet.
        
       | perryizgr8 wrote:
       | > 107 minute read
       | 
       | Hmmm... that is truly way too deep.
        
       | pictur wrote:
       | As someone who has never played a pass, I have a question. Every
       | article I see about rust says it's all very simple. this is
       | something that can change from person to person yes, but is it
       | like that for you? I wonder what people who play with rust think
       | about it. (this is just a question please don't take it as an
       | offensive comment)
        
         | umanwizard wrote:
         | IMO Rust is much easier and simpler than C++, but much harder
         | and more complex than any managed GC language.
        
         | grumblenum wrote:
         | It's a west-coast tech industry consortium language that is a
         | mashup of subsets of SML, C++ and Cyclone with some very strong
         | opinions about aesthetic differences. "Simple" is not a good
         | description. Lisps are simple. Rust, Java, C++ & friends are
         | not. Also, the core paid promoters of rust are almost all
         | alumni of the Ruby on Rails hype-train. You can judge for
         | yourself if their promotion of rails was grounded in truth.
        
           | moldavi wrote:
           | Rust has paid promoters?
        
             | steveklabnik wrote:
             | We do not, but sometimes people who want to disparage me,
             | Rust, or both, say stuff like this. It was said a lot more
             | often in the earlier days.
        
               | grumblenum wrote:
               | Well, I certainly wasn't speaking about you specifically.
               | However, did you not say that language promotion is what
               | you do and that you quit Mozilla for not paying you
               | enough for it here:
               | https://steveklabnik.com/writing/thank-u-next ?
        
               | steveklabnik wrote:
               | "promotion" doesn't appear in that post. My job was
               | writing documentation. I did say that I was considering
               | moving into evangelist/growth roles. My next job wasn't
               | those two though, it was PM.
               | 
               | I guess you were trying to disparage Yehuda, then?
        
               | grumblenum wrote:
               | I mean that you are one of several. Hence the running
               | joke is the "Rust Evangelism Strike Force" and not "that
               | one rust guy."
               | 
               | An evangelist/growth focused role is exactly what a
               | promoter is. If you spend large numbers of typical work-
               | hours, which I'm assuming you're paid for, on social
               | media to promote something, like Rust, or you go to
               | conferences and events to speak publicly in promotion of
               | something like Rust, then a "paid promoter" seems like a
               | pretty accurate description.
               | 
               | Is that disparaging? Is that not what you are doing right
               | now?
        
               | steveklabnik wrote:
               | > An evangelist/growth focused role is exactly what a
               | promoter is.
               | 
               | Right. _move into_. Because that was not a part of my job
               | description at Mozilla. They didn 't dislike the stuff I
               | was doing, but it's not my _work_.
               | 
               | The disparagement is the implication of lying:
               | 
               | > You can judge for yourself if their promotion of rails
               | was grounded in truth.
        
               | grumblenum wrote:
               | Given a 15 year retrospective, would you say that Rails
               | lived up to the hype? If you think so, then there is
               | nothing "disparaging" in that comment at all. If you
               | don't think so, then doesn't that mean a reader should
               | not regard such promotion with confidence? No need to be
               | defensive.
        
           | floatboth wrote:
           | SML doesn't have typeclasses/traits, so it's fair to say
           | "Haskell" instead.
        
             | grumblenum wrote:
             | SML has signatures. In the absence of HKT, I think it's not
             | a fair comparison. Most popular languages have interfaces
             | even if not by that name. Haskell, Scala and C++ are
             | special in that you can describe an interface for
             | interfaces. I'm sure I'm not giving due credit to others.
        
         | Argorak wrote:
         | (For context: I teach Rust since 2015)
         | 
         | Rust is "simple" in that it is based on a few solid principles.
         | Data with layout, Ownership of the data and the distinction
         | between sharing and mutability (through immutable references
         | and mutable references). Most of the interesting API in _some_
         | way leans into that. So, the _basics_ of Rust are indeed quite
         | constrained.
         | 
         | But that's "simple" in the sense of "Ruby is simple, everything
         | is objects and message passing".
         | 
         | But then: Generics introduce a ton complexity on top, because
         | they open a space where you are _potentially_ in a borrowed
         | _or_ an owned situation. There's tons of tiny optimisations and
         | protocols: for example that `Box<[u8]>` exists and turning it
         | into `Vec<u8>` comes basically for free. There's tiny details
         | like compiler assists that sometimes trigger and sometimes not.
         | But even Generics can also be seen as an application/extension
         | of the basic principles.
         | 
         | And then, there's tons of tooling and language that you need to
         | know. Standard traits and features of the stdlib. Clever
         | applications of Ownership based management for handles.
         | 
         | And particularly Rust is (currently) not _easy_, because the
         | programming environment has fundamentals that are unusual to
         | program that you cannot bypass as a beginner. That actually
         | changes, the more those practices get known to people and
         | individuals can teach each other rather than finding out
         | themselves.
         | 
         | I think what people say when they say "Rust is simple" is that
         | they can break down any house to a limited set of bricks. That
         | is certainly true. But that needs a level of proficiency with
         | the language. So, telling beginners "look at Rust, it's simple"
         | is harmful - it's full of combinations and applications of its
         | language core and it will take quite some time to learn
         | breaking that down for people and figure out which tool is for
         | which moment.
        
         | fasterthanlime wrote:
         | "Simple" can mean many things.
         | 
         | For me Rust is "simple" because it has great tooling / great
         | diagnostics, so when I get something wrong, most of the time it
         | can tell me exactly what it is (and how to fix it!), and only
         | occasionally does it send me down a rabbit hole.
         | 
         | Rust is also "simple" because it's memory-safe - if I can get
         | something to compile (without unsafe code), then I know there's
         | no use-after-free, double-free, buffer overflows, etc. I can
         | concentrate my mind on the business logic (where bugs still can
         | and do happen).
         | 
         | Finally, Rust is "simple" because it lets me build abstractions
         | so I can see more clearly what I'm doing. At work we had a Go
         | codebase full of goroutines and channels - I got tired of it
         | and did a proof of concept in Rust: now it's just one async
         | Stream, you can see the data flow clearly and worry about each
         | stage of the pipeline in isolation, instead of having to jump
         | across many different source files.
         | 
         | But no, it's not all very simple. There's plenty to learn, and
         | some of the core concepts (ownership / lifetimes) are fairly
         | hard to wrap your mind around, even if you've encountered
         | something similar in other languages. My take is that it's
         | worth it - you'll be very slow at first, but as you get more
         | comfortable with it, so will you get faster.
        
         | dicroce wrote:
         | I don't think it's simple. I find lifetimes especially
         | challenging. I love rust, but sometimes I am remarkably
         | unproductive in it because the compiler is so strict. Honestly,
         | sometimes I think I need flash cards for memorizing how to deal
         | with particular compiler errors.
        
           | Argorak wrote:
           | Lifetimes are an interesting illustration. They are
           | conceptually surprisingly simple (they draw regions in time
           | and check if one region is clearly smaller than another), but
           | as a language feature very weird, as they are essentially a
           | declarative language in an imperative setting. A lot people
           | struggle with them more because they can't really _place_ the
           | feature rather than applying it.
        
         | hmfrh wrote:
         | > this is something that can change from person to person yes,
         | but is it like that for you?
         | 
         | In my opinion it really depends on your background.
         | 
         | For example, Rust uses the `i32` type as the standard integer.
         | If you come from a C/C++ background this is probably not that
         | weird, you probably know how many bits there are in 4 bytes off
         | hand, there's `int32_t` standard types and you probably have
         | experienced not wanting an integer with a variable size that
         | depends on the platform.
         | 
         | If however you come from Python, Javascript, or maybe even
         | Java, this might range from a little weird to very weird.
         | Python only has the `int` type, no unsigned types and you might
         | not know how many bits make up a "standard" integer, or even
         | exactly how bits, signedness and integer sizes fit together.
         | Java doesn't have unsigned types and doesn't have issues with
         | sizes depending on the exact platform, so they might not
         | understand why you would want the amount of bits right there in
         | the type name.
         | 
         | This is just for the standard integer type. If you consider how
         | many design decisions are a direct result of working on low
         | level, high correctness required C++ code, the "simplicity"
         | could range from completely simple to very much not simple.
        
         | [deleted]
        
       | [deleted]
        
       | aarongolliver wrote:
       | Not sure a better place to ask this, but we're getting to the
       | point in most OSs where basically anything you want can finally
       | be done truly async (non-blocking).
       | 
       | Except that DNS apparently still blocks[0], so usually things
       | farm DNS requests off to their own blocking thread pools (the
       | author ends up disabling DNS just so they can "prove" everything
       | works with just a single thread)
       | 
       | What's so fundamentally difficult about writing an async/non-
       | blocking DNS resolver? Is it just a lack of a real need for it?
       | 
       | [0] (quote: People have been trying to build asynchronous DNS
       | resolvers for decades without success. Can it be done? Yes. Has
       | it been done? No. )
       | https://gist.github.com/djspiewak/46b543800958cf61af6efa8e07...
        
         | aarongolliver wrote:
         | I have found this comment on liburing[0] which clarifies some
         | things for me. So is it because something like getaddrinfo is
         | not just a simple operation, but a hodgepodge of stuff like
         | "first try nscd, then read resolv.conf, then make a bunch of
         | requests using one of many different protocols"?
         | 
         | I think I have a lot more to learn about DNS...
         | 
         | [0]
         | https://github.com/axboe/liburing/issues/26#issuecomment-738...
         | 
         | > [re: implementing getaddrinfo] It's not planned because it's
         | not a single operation but a complex beast reading files,
         | exchanging messages, etc. IMHO, as it's not a single operation
         | it fundamentally doesn't fit liburing, but implementing under
         | some framework on-top would be more viable.
        
         | jgehrcke wrote:
         | There is real need, and there are solutions.
         | 
         | NodeJS has a built-in DNS resolution API which is fully async /
         | libuv-cooperative:
         | https://nodejs.org/api/dns.html#dns_dns_resolve_hostname_rrt...
         | 
         | Other async runtimes bundle libs like c-ares to implement non-
         | threadpool-based async DNS.
         | 
         | quoting the nodejs docs: "These functions are implemented quite
         | differently than dns.lookup(). They do not use getaddrinfo(3)
         | and they always perform a DNS query on the network. This
         | network communication is always done asynchronously, and does
         | not use libuv's threadpool."
        
         | otabdeveloper4 wrote:
         | On the OS level there is no difference between "sync" and
         | "async". They become different in programming language
         | runtimes.
        
           | lights0123 wrote:
           | Not really. mkdir(2) is definitely sync, io_uring has been
           | called asynchronous since the start, and its predecesor has
           | asynchronous in the name: https://lwn.net/Articles/776703/
        
           | aarongolliver wrote:
           | There is a difference between blocking and non-blocking,
           | which is what I am asking about. Language runtimes apparently
           | cannot provide non-blocking DNS (as evidenced by this
           | article, and the other one I linked which was on HN a week or
           | so ago).
           | 
           | DNS seems like it perfectly fits the model of "give me a
           | buffer (to hold the resolved IP), and I will wake you up when
           | it's filled". Maybe io_uring (/ IOCP?) already can support
           | this, and these articles are just mistaken about the current
           | (or soon-to-be) state-of-the-art? Or is there some
           | fundamental reason about DNS that make writing a non-blocking
           | resolver very very difficult?
           | 
           | It's just very odd to me that userspace apps are creating
           | their own blocking thread pools just to run DNS stuff, when
           | they can do seemingly everything else with just a single
           | thread if they wanted to.
           | 
           | (edited a few times, sorry if I caught someone who was mid-
           | reply)
        
       | layoutIfNeeded wrote:
       | What an obnoxious style of writing.
        
         | masklinn wrote:
         | Don't read it? Some people enjoy amos' style, others don't.
         | 
         | If that's not your cup of tea you can just gravitate other
         | presentation styles, it's not like that's in short supply.
        
         | libria wrote:
         | > What an obnoxious style of writing.
         | 
         | It's good to see counter viewpoints but share what you or
         | others would be looking for instead.
         | 
         | "This sucks" isn't helpful.
         | 
         | "This part sucks but would be awesome if it {did this}" is.
        
         | codezero wrote:
         | I usually can't keep my focus in an article that is so long,
         | but the narrative style helped keep me hooked through the
         | entire thing.
        
       | Subsentient wrote:
       | The problem with analyzing stuff like this about Rust, is that
       | even Rust itself has no idea how it works, or what syntax and
       | semantics actually mean.
       | 
       | Rust doesn't merely lack a language standard, its reference and
       | advanced documentation is also a very unfunny joke. You can learn
       | just enough to have some idea of what you're doing, and then
       | you're stuck huffing unicorn queefs because nobody including the
       | compiler devs knows the exact rules on how syntax is supposed to
       | work.
       | 
       | Magical shortcuts, syntax, and borrow checker tricks are added
       | and removed at random with no rhyme or reason, and Rust severely
       | punishes the programmer for wanting to know how the language
       | actually works, since there are usually no answers, or worse,
       | wrong answers, for them to find.
       | 
       | I still use Rust because the memory safety without a GC is
       | wonderful, but please, don't even try to make it look like Rust
       | has some official documentation or behavior. It most definitely
       | does not, and what really disturbs me is how so many of the Rust
       | community _don 't even want a standard_.
       | 
       | So OP, everything you've just done, will be rendered obsolete and
       | woefully wrong in a couple years when Rust serves up another
       | opaque unicorn-burger.
        
         | pitterpatter wrote:
         | >Magical shortcuts, syntax, and borrow checker tricks are added
         | and removed at random with no rhyme or reason
         | 
         | Everything else aside for now, this is just patently false.
         | There's a well-established RFC process for any such changes to
         | the language/compiler/standard library.
        
         | nynx wrote:
         | What? Are you sure you're looking at the right language? Or
         | maybe you've confused stable rust and nightly rust?
        
           | Subsentient wrote:
           | No, I'm definitely on the right language. For example, lots
           | of magical implicit reborrows with fuck all in the
           | documentation, pattern matches and specifiers with no formal
           | behavior defined, lints that change constantly. I do use
           | nightly generally, simply because stable is crippled in
           | features, but the same issues apply to stable.
           | 
           | I should mention that even when they adjust these things in a
           | stable release, they never fix the documentation to reflect
           | it. And in the case of many of these things, they aren't
           | documented at all, except perhaps a couple sentences about
           | their existence.
        
             | myrrlyn wrote:
             | PRs welcome!
        
               | Subsentient wrote:
               | These issues cannot be fixed by merely updating the docs
               | for a few items. (and frankly I don't contribute much to
               | big FOSS projects because I've had bad experiences with
               | big projects before)
               | 
               | This can only be fixed by A. _Requiring_ that all syntax
               | changes, no matter how small, are _fully_ documented
               | _before_ they reach stable or even beta, and B. Eventual
               | standardization, even if the reference implementation
               | ends up being more cutting edge.
               | 
               | Rust devs also need to think more about how they're going
               | to document a feature before they add it. Like for many
               | of the implicit reborrows, they're done in non-obvious
               | places and not done in others, with no pattern to where
               | it's done and where it's not. That kind of implementation
               | is _extremely_ painful to document or standardize. Rust
               | needs to think more in terms of _rules_ for syntax,
               | rather than instances of a particular shortcut. Rather
               | than adding a standalone instance, you should edit the
               | rules so that instance is covered.
        
               | ben0x539 wrote:
               | Are you interested in standardization for its own sake or
               | as a forcing function for exhaustive documentation?
        
               | Subsentient wrote:
               | Both, though mostly documentation. To me, a standard _is_
               | the documentation. The fact I have to read rustc source
               | to find syntax is frankly an abomination. A standard
               | defines syntax and grammar in ways that a html reference
               | page _never_ does. It doesn 't matter as much for
               | "fluffy" languages like Python or Lua, but it's very
               | important for a systems language.
               | 
               | I also want standardization to make writing alternative
               | conforming compilers easier. I strongly dislike the
               | attitude of some Rust maintainers, and a standard is a
               | useful tool to forcibly remove some level of control from
               | their hands.
        
               | CJefferson wrote:
               | While this rant is excessive, I do see some of the
               | annoyance, for example looking at: https://doc.rust-
               | lang.org/core/cmp/trait.PartialOrd.html
               | 
               | The top talks about lt, le, gt, ge, but then the docs
               | below talk about '<' and '>', which I assume are lt and
               | gt? But then there seems to be no requirements in le and
               | ge? I assume everything needs to be consistent?
               | 
               | Also (relating to auto-ref), why does 'a<b' work, when
               | the trait takes a reference?
               | 
               | EDIT: Some of this is revealed if I scroll down and look
               | at the definitions of 'lt' and friends, but I'm already
               | pretty confused at the top.
               | 
               | EDIT 2: This really wasn't worth the effort of defending,
               | but I made an issue with my comments on PartialOrd.
        
         | [deleted]
        
         | bob1029 wrote:
         | Documentation and roadmap are the 2 make-or-break things I look
         | for in a language/framework/tool. If one of these things is not
         | satisfied, I have an incredibly hard time convincing myself to
         | invest time.
         | 
         | I have taken a look at the Rust documentation, and I just can't
         | see myself participating at this point because of it. The
         | roadmap is also unclear to me. I google 'Rust Roadmap', and
         | guess what the first result is?
         | 
         | https://rust.nolt.io/roadmap
         | 
         | So far a video game's roadmap is ranked above that of this
         | language. I had to search for 'rust-lang roadmap' to get a good
         | top hit... Which brought me here:
         | 
         | https://blog.rust-lang.org/2020/09/03/Planning-2021-Roadmap....
         | 
         | > The core team is beginning to think about the 2021 Roadmap,
         | and we want to hear from the community. We're going to be
         | running two parallel efforts over the next several weeks: the
         | 2020 Rust Survey, to be announced next week, and a call for
         | blog posts.
         | 
         | A call for blog posts. Beginning to think about...
         | 
         | As a .NET developer, this honestly sounds like a dumpster fire
         | to me. Who is actually in charge? What is the vision for 2025?
         | How do you explain your software technology roadmap to your
         | customers when you are using Rust and have to operate in
         | contract lifetimes of 5+ years?
         | 
         | Clearly, I don't know the first goddamn thing about the Rust
         | ecosystem or why I would want to use it. I am just a dirty .NET
         | plebian looking in from the outside. It doesn't look very
         | compelling right now. Can someone correct my perspective on
         | this? I am open to it. There must be some value I am missing.
         | Maybe Rust just isn't for the "enterprise", or whatever boring
         | niche box I seem to be stuck living in these days.
         | 
         | There are obviously use cases and happy developers out there.
         | Don't let me get in your way.
        
         | myrrlyn wrote:
         | every time this comes up i just have to ask
         | 
         | - what do you think a language standard is or does
         | 
         | - what languages with standards do you think standardization
         | has helped (this is a trick question! do not answer "c" or
         | "c++"!)
         | 
         | - in which languages with standards is a plurality of code
         | written to that standard? (this is also a trick question! do
         | not answer "c", "c++", or "javascript"!)
        
           | nicebyte wrote:
           | a "language standard" is something i can use to hold
           | implementation developers accountable. it doesn't matter if
           | it's an actual international standard, or just a document on
           | the internet. I want a dry, exhaustive and systematic
           | description of the features and intended behaviors. Refer to
           | the Vulkan spec
           | (https://www.khronos.org/registry/vulkan/specs/1.2/html/) to
           | get an example of what i consider a good specification.
           | 
           | The rust docs don't currently meet that bar. They're quite
           | WIP, and I imagine someone hitting a particular edge case
           | will not necessarily be well equipped to understand whether
           | the behavior they're observing is intended, a bug in the
           | implementation or something else entirely.
        
             | mjw1007 wrote:
             | Yes.
             | 
             | There is a great deal of written information about Rust-
             | the-language which exists only in compiler comments, RFCs,
             | bug reports and internals.rust-lang.org threads (often
             | slightly stale in all cases).
             | 
             | It's Rust's greatest weakness.
             | 
             | But "every time this comes up" the discussion somehow gets
             | sidetracked into arguments about standardisation, even in
             | cases (such as this thread) when it's clear that what the
             | ranter cares about is accurate documentation.
        
           | nicebyte wrote:
           | and to answer your other questions - Java and Python both
           | have what I consider good, detailed specifications (which one
           | could call "standards").
        
           | pittmajp wrote:
           | This is silly. That's like saying that speed limits don't
           | work because everybody goes over them. Even if people don't
           | 100% comply with standards, the fact that they exist reigns
           | in peoples' behavior more so than if there wasn't one at all.
        
         | jokethrowaway wrote:
         | I assume your criticism revolves around breaking changes as
         | what you mention is simply not true; there is plenty of
         | documentation and language definition and I never found an
         | issue.
         | 
         | Development languages evolve all the time. It's normal, the
         | same happens to human languages.
         | 
         | I can't even say Rust is changing that much (especially
         | compared to JS, my main work language).
        
       ___________________________________________________________________
       (page generated 2021-07-26 23:01 UTC)