[HN Gopher] The Rust I wanted had no future
       ___________________________________________________________________
        
       The Rust I wanted had no future
        
       Author : dochtman
       Score  : 802 points
       Date   : 2023-06-05 07:03 UTC (16 hours ago)
        
 (HTM) web link (graydon2.dreamwidth.org)
 (TXT) w3m dump (graydon2.dreamwidth.org)
        
       | titzer wrote:
       | > Library-defined containers, iteration and smart pointers.
       | Containers, iteration and indirect-access operations (along with
       | arithmetic) comprise the inner loops of most programs and so
       | optimizing their performance is fairly paramount; if you make
       | them all do slow dispatch through user code the language will
       | never go fast. If user code is involved at all, then, it has to
       | be inlined aggressively. The other option, which I wanted, was
       | for these to be compiler builtins open-coded at the sites of use,
       | rather than library-provided. No "user code" at all (not even
       | stdlib). This is how they were originally in Rust: vec and str
       | operations were all emitted by special-case code in the compiler.
       | There's a huge argument here over costs to language complexity
       | and expressivity and it's too much to relitigate here, but .. I
       | lost this one and I still mostly disagree with the outcome.
       | 
       | Trust me, you do _not_ want to put this stuff into the compiler.
       | It 's not _just_ that it 's cheating for perf, but it's confusing
       | to users (no source code to read how these work) and frustrating
       | that they can't write their own. Ultimately what this really
       | means is that these things are indeed written in _some_ language
       | --the compiler 's _IR_. JavaScript actually has a ton of this
       | kind of things and every JS engine has gone through multiple
       | generations of  "what language do we write Array.sort in!?". In
       | V8, these intrinsics are written in a DSL because doing them in
       | asm, a special dialect of C++, or one of two compiler IRs ended
       | up being more trouble than its worth.
       | 
       | You want a clear separation between what is language and what is
       | library.
        
         | marcosdumay wrote:
         | Almost all high-level languages have builtin containers. I do
         | think it's a bad fit for Rust (at least for what Rust became),
         | but it's not the end of the world by itself.
         | 
         | The fact that JS has about 3 of them (is it only 3? I'm not
         | sure of this), with different interfaces, and non-usual
         | behavior is what creates problem there. But other languages
         | quite successfully make their own containers either different
         | enough or similar enough that it's not a problem.
        
           | titzer wrote:
           | > The fact that JS has about 3 of them (is it only 3? I'm not
           | sure of this), with different interfaces, and non-usual
           | behavior is what creates problem there.
           | 
           | V8 has over 35,000 lines of Torque, the DSL for describing
           | these kinds of built-ins. Big chunks of that are dealing with
           | Array methods like splice, sort, from, foreach, etc; and then
           | there is stuff like BigInt, arguments object, a zillion
           | methods on strings.
           | 
           | > Almost all high-level languages have builtin containers.
           | 
           | I don't think that's really true. Maybe for Python and Ruby,
           | and other dynamically typed languages. I dunno about Swift.
           | But it's not really true for Java or C#. Those languages have
           | their collection/containers libraries written in themselves.
           | Java has _other_ problems separating the language from the
           | libraries, but it 's not due to collections.
        
           | paulddraper wrote:
           | Array
           | 
           | Map
           | 
           | Set
           | 
           | WeakMap
        
             | dexwiz wrote:
             | TypedArrays are also their own thing. But unless you are
             | reading binary files/WASM, most JS devs don't use them.
             | 
             | https://developer.mozilla.org/en-
             | US/docs/Web/JavaScript/Guid...
        
         | dermesser wrote:
         | I read this as wanting a different "feeling" for the language.
         | Go uses this approach, with maps and arrays being baked into
         | the syntax, and things work just fine. Implementing Vec in user
         | code is rather the C++ "feeling".
         | 
         | Personally I'm happy Rust took the C++ route, it makes things
         | more interesting, but I can see his point.
        
       | smitty1e wrote:
       | [flagged]
        
       | camgunz wrote:
       | I would love to play with the language Graydon describes here.
        
       | Aissen wrote:
       | On integer wrapping, I think explicit wrap is annoying at first,
       | but eliminates a whole class of bug. I can only agree with:
       | 
       | > (Swift at least traps in release by default -- I wish Rust had
       | chosen to).
       | 
       | I enable it in release on serious projects:
       | [profile.release]         overflow-checks = true
        
         | wongarsu wrote:
         | At least Rust has left the door open to allow panicking on
         | overflow even in release builds in the future - presumably on
         | platforms where hardware support makes this cheap enough.
        
         | jedisct1 wrote:
         | And then you may just have introduced side channels in crypto
         | code.
        
           | Aissen wrote:
           | > And then you may just have introduced side channels in
           | crypto code.
           | 
           |  _incorrect_ crypto code. If overflow is intentional, it
           | should be annotated as such in operations, will generate
           | similar assembly and won 't panic. If it isn't intentional,
           | then the code was bad to begin with.
        
             | dooglius wrote:
             | If the branch can be mispredicted, and the misprediction
             | happens depending on internal state you don't want to leak
             | (cf. Spectre), then you have a side channel even if the
             | branch is never actually taken.
        
         | layer8 wrote:
         | The correct (but hard) way to prevent overflow-related bugs
         | would be to insist on the compiler being able to prove that no
         | overflow can occur. Basically the same thing you do in your
         | head to convince yourself that the program is correct and won't
         | overflow, only in a more formally rigorous fashion. Modulo
         | semantics by itself doesn't prevent bugs.
        
           | the_duke wrote:
           | Rust has an optional clippy lint that will warn/forbid all
           | basic arithmetic that might over/underflow, forcing you to
           | use dedicated methods with explicit behaviour instead.
           | 
           | It's very annoying , but I use it in code where this is
           | critical.
        
       | dathinab wrote:
       | One thing I want to add wrt. "Cross-crate inlining and
       | monomorphization. I wanted crates to allow inlining inside but
       | present stable entrypoints to the outside." [..] is that it was
       | in general well desired AFIK by most developers but so far out of
       | scope that you probably would need to had the resources rust has
       | today _before_ the 1.0 release to get it done right. At lest with
       | the state CS research had been at during that time. By now due to
       | various reasons (e.g. swift) there is much more research in that
       | direction already done hence a new language has it much easier.
        
         | estebank wrote:
         | I think this is something that Rust might try again at some
         | point, as part of a stable rust to rust ABI.
        
           | dathinab wrote:
           | The problem is that it's not that simple to make it work
           | nicely you need to design you language around such
           | constraints. Trying to do it the other way around is even
           | harder.
           | 
           | For example rust in it's current design relies quite a bit on
           | the combo of: generics monomorphisation + inlining + dead
           | code elimination. But this reliance doesn't play well with an
           | stable ABI. Similar features like generic associated types
           | and similar do not make the story easier. Like an rust stable
           | ABI likely would only support `dyn Trait` and no generics or
           | "on the fly" provide a `dyn` variant for generic method where
           | possible. But even that isn't really good enough for a lot of
           | use cases in a lot of different ways. Additionally a ton of
           | important rust things are not `dyn` compatible at all.
           | 
           | Even some of the "easy" to solve things aren't that easy for
           | non-technical reasons. E.g. a lot (but not all) of `impl
           | Into<T>`, `impl AsRef<T>`, `impl Borrow<T>` etc. cases are
           | best handled for a stable ABI context by aplying the single
           | function of the trait (`.into()`,`.as_ref()`, etc.) _before_
           | calling the function and only having an ABI stable version
           | for that. The side of which traits qualify for this is easy
           | (you annotated the traits) the "when not apply it even if it
           | seems valid" part isn't that easy not for technical reasons
           | but for communication/documentation/avoiding unexpected
           | outcomes reasons.
        
       | MontagFTB wrote:
       | > Not 100% clear about actors -- I was weirdly focused on that
       | model that in practice has many issues...
       | 
       | Any ideas what he meant here?
        
       | aidenn0 wrote:
       | I wanted nearly the same language Gradyon wanted, but I also
       | suspect that my tastes are insufficiently mainstream for a
       | language to my tastes to be as popular as Rust has become (much
       | less as C++ is).
        
       | garbagecoder wrote:
       | >a viable c++ alternative
       | 
       | Wow, so one of the principal creators understands that that's
       | what it is, not a replacement for everything under the sun.
       | 
       | And it's a GREAT c++ alternative, but ffs please stop telling me
       | to use it for everything from webdev to embedded.
        
       | Keyframe wrote:
       | The Rust he wanted sounds a lot like Ada.
        
         | tejinderss wrote:
         | Or like ocaml with its module system and no explicit lifetimes
         | / & type.
        
           | lawn wrote:
           | Which reminds me, I really need to try out ocaml.
        
             | giraffe_lady wrote:
             | A really unknown and underrated language for how good it is
             | right now is rescript. It's ocaml's type & module systems
             | and runtime semantics grafted onto JS. Ocaml's tooling,
             | ecosystem, and standard lib situation are all ...fine...
             | but quirky in ways that can be barriers when learning it.
             | 
             | Rescript lets you skip most of those, plus the (overblown
             | but also real) weird syntax. I like ocaml and use it for
             | projects still but if you just want to play around with
             | what's unique and strong about it, rescript is what I would
             | recommend right now.
        
           | glandium wrote:
           | The original rust compiler was written in ... Ocaml.
        
       | qaq wrote:
       | Will be interesting to see how things play out in Mojo as it
       | looks like they will account for lessons learned by Rust
       | community.
        
       | cmrdporcupine wrote:
       | When I first read Graydon talking about his plans for Rust, I
       | pictured it as StandardML (or OCaml) without a garbage collector,
       | and I was sold. This list doesn't look fully like that, but is
       | closer than current Rust is.
       | 
       | My interest back then was in a higher level language with type
       | inference and a modern ML-like type system but that could be used
       | for systems programming, especially in database, virtual machine,
       | and even operating system dev.
       | 
       | These days I work pretty much full-time in Rust, and I think Rust
       | as it is today delivers on _some_ of that promise, but not all. I
       | feel like the language 's borrowing and ownership checking are
       | pretty brilliant _but_ really begin to become a pain when dealing
       | with nested and interrelated trees of objects and iterators (like
       | if building a compiler or query evaluator, etc.), and resorting
       | to Arc /Rc/RefCell, etc. feels awkward.
       | 
       | I'm not sure if the language Graydon talks about here would have
       | been better for that or not.
       | 
       | But I'm also happy we have Rust, because it's an improvement over
       | what else is out there, and I hope the community gets through its
       | growing pains.
        
       | javajosh wrote:
       | Too bad graydon didn't get his way with build times. I've come to
       | believe that the most important feature of any development
       | environment is to minimize the built-test-debug cycle. Of course,
       | a real system has many different ones, ranging from "language
       | level" to "I have to redeploy and perform a complex series of
       | actions in an app or 3". But at the language level I've found
       | that _build performance_ is of paramount importance. And when a
       | project ignores build perf, _everyone suffers every time they
       | build_. Since the lower-limit is the language compiler, it should
       | therefore be kept very fast. (And the people working on the
       | applications must _constantly resist_ adding features that slow
       | the BTD loop down any further.)
       | 
       | A good example of this trade-off in Java is Lombok. A very handy
       | library that legitimately avoids a ton of boilerplate, but it
       | also absolutely tanks your build time. In a real system, a large
       | one, your team is better off just getting good enough with their
       | editor that they can generate the hateful boilerplate, and leave
       | Lombok out. Because you'll be paying for Lombok all the time, and
       | only need it a small fraction of the time. There are hundreds,
       | thousands of these conveniences that are deeply tempting but
       | should be avoided, in every build. The problem is that the
       | programmers become attached to these little nicities and actively
       | resist giving them up, even though they are so costly.
        
         | zamalek wrote:
         | I don't understand the big issue with build times: I have
         | always found that they are almost instant for incremental
         | builds (assuming you use lld or mold).
        
           | umanwizard wrote:
           | > I have always found that they are almost instant for
           | incremental builds (assuming you use lld or mold)
           | 
           | They are not almost instant once your project grows to a
           | certain size, which is still well within the bounds of a
           | realistic single company's project (I work on Materialize
           | which is all in rust).
        
             | zamalek wrote:
             | And you have it set up with multiple compile units, shared
             | generics, and lld (or mold ideally)?
        
               | umanwizard wrote:
               | > multiple compile units
               | 
               | Yes
               | 
               | > shared generics
               | 
               | I don't know what that is. Some googling suggests that
               | it's a nightly-only feature, whereas we use the stable
               | compiler. If it's something that can be done in stable,
               | I'd appreciate a pointer.
               | 
               | > lld (or mold ideally)                   $ echo
               | $RUSTFLAGS         -C link-arg=-fuse-ld=lld -C
               | debuginfo=0
        
           | duped wrote:
           | Then you're not working on big enough projects or in
           | ecosystems where you need to regularly perform clean
           | compiles, because afaik the "incremental" output from
           | cargo/rustc isn't relocatable.
        
           | bluGill wrote:
           | In C++ a small change to a header can result in having to
           | rebuild thousands of different files. Even if each file
           | builds fast the total can be long. I have also benchmarked
           | including a specific header (not using it, just including it)
           | costs .5 seconds which adds up quick in those thousand files.
           | Another benchmark found a specific boilerplate code construct
           | added .1 seconds to the time to compile the file each time
           | you added that one line - and it was a line commonly
           | repeating in a header (MOCK_METHOD from gmock - there is a
           | FAQ entry on how to get this time down)
        
         | duped wrote:
         | On top of this, there's this bogus mantra in the Rust community
         | that "if it type checks then it works."
         | 
         | I've spent of hours interactively debugging Rust code, crawling
         | through tracing output etc, and hours waiting for Rust code to
         | link with mold because I added an eprintln somewhere, so I'm
         | not convinced.
         | 
         | I love writing Rust and think it makes my life easier, but
         | there's this stockholm syndrome about compile times. It sucks,
         | and it's not the type system or borrowchecker's fault.
        
           | mrkeen wrote:
           | > there's this stockholm syndrome about compile times. It
           | sucks, and it's not the type system or borrowchecker's fault.
           | 
           | What do you mean?
        
             | duped wrote:
             | The bottlenecks in Rust builds arent't type checking/borrow
             | checking. It's things like macro expansion, module
             | resolution and compilation, codegen from LLVM IR, and
             | linking. Some of these things have workarounds like
             | sccache, using mold instead of the system linker, etc but
             | others are problems that not only remain unsolved but are
             | actively getting worse in the ecosystem.
        
           | dralley wrote:
           | >hours waiting for Rust code to link with mold because I
           | added an eprintln somewhere
           | 
           | How is that Rust's fault?
        
             | saghm wrote:
             | I think the "with mold" comment is expressing that even
             | with the state-of-the-art fastest linker that's generally
             | available for use with Rust, it still is slow. If you used
             | the default linker, it would be even slower.
        
         | throwaway2037 wrote:
         | I'm not here to advocate for Lombok. However, you are first
         | that I saw here to complain about much slower compile times.
         | Can you teach me more? On a multi-core 5GHz desktop PC with
         | 64GB RAM, who is really thinking about Java compile times these
         | days? And I have worked on 1M+ line projects. Sure, the first
         | compile is slow, but after, everything is incremental.
        
           | imtringued wrote:
           | Nobody complains about the Java compiler. Everyone complains
           | about the class loading performance.
        
           | javajosh wrote:
           | It's not the compiler, in this case. Lombok generates code,
           | and for reasons (which I have since forgotten) it _always_
           | generates code and invalidates incremental builds in gradle,
           | for example, at least when used in combination with ORM
           | annotations. This happened at a previous contract on a
           | company computer so I don 't have the links, but IIRC the
           | gradle docs mention the interaction(s).
           | 
           | I wanted to delombok to save 30s off of each build, but the
           | rest of the team(s) refused to let it go. I thought, and
           | still think, that was a short-sighted mistake.
        
         | naasking wrote:
         | > I've come to believe that the most important feature of any
         | development environment is to minimize the built-test-debug
         | cycle.
         | 
         | I think the next innovation in statically typed languages
         | should be to somehow break the build-test-debug cycle. We
         | basically have the same interface to programming as dynamically
         | typed languages, despite having considerably more information
         | available because of the types. This should be exploitable
         | somehow to give typed languages more advantages, and change the
         | very nature of the loop.
        
       | xiphias2 wrote:
       | ,,I would have traded performance and expressivity away for
       | simplicity''
       | 
       | ,,A lot of people in the Rust community think "zero cost
       | abstraction" is a core promise of the language. I would never
       | have pitched this and still, personally, don't think it's good''
       | 
       | If the language makes compromises in performance, it's not a real
       | C++ competitor anymore.
       | 
       | Some things are not about what people ,,like'', but that we need
       | a language that is safe and can compete with C/C++ in performance
       | for systems level programming, as most security problems in the
       | world come from C/C++ memory management.
       | 
       | If it's significantly slower than C++, Mozilla couldn't have
       | picked it up to replace C++ code base, as there was a huge
       | competition in performance between browsers.
        
         | avgcorrection wrote:
         | I would have had much less interest in Rust as a peanut gallery
         | participant if it didn't have that goal of competing with C++.
         | Not because I am a C++ or C programmer myself, but because it
         | has been interesting to see if alternatives to such well-
         | established languages can in fact be successful.
         | 
         | Think about it: we have dozens upon dozens of Hoare-esque
         | "sacrifice some performance for ergonomics" languages out
         | there. But what are you gonna do when you need performance that
         | the language is too inflexible for? Hmm... perhaps write some
         | of your app in C...?
         | 
         | If it weren't for Rust and similar languages, we would never
         | have a hope of moving on to more modern languages for those
         | "can't do this in my $mainlang due to performance" problems.
         | How would yet another language that (according to Hoare):
         | 
         | > and I would have traded lots and lots of small constant
         | performancee costs for simpler or more robust versions of many
         | abstractions.
         | 
         | have helped with that? It wouldn't! You would have still been
         | stuck with having to bind to C, C++, assembly, or whatever else
         | sufficiently "zero-cost abstraction" language.
        
         | bo1024 wrote:
         | Yeah, I think most of this is implied in the article.
        
         | bluGill wrote:
         | Rust does make compromises of performance. That is not always
         | bad so long as the compromises are minor. Fortunately Rust can
         | do most of the checking at compile time, but it if you read
         | from an empty vector Rust doesn't have undefined behavior and
         | that means there is a runtime check of some sort in at least
         | some cases.
         | 
         | The trick is to find the right place to compromise so the cost
         | is minimal overall even if it isn't zero.
        
           | tialaramex wrote:
           | The thing is, although the check looks like work, a _correct_
           | C++ solution almost always needs to do the same work. There
           | are real world cases where it doesn 't, but lots more where
           | in C++ we need to explicitly do the work or our program
           | malfunctions, sometimes in subtle ways - while in Rust we get
           | this right by default.
        
             | bluGill wrote:
             | There are cases where you as the programmer have enough to
             | know that there is data in the container even though
             | anything less than whole program analysis cannot prove it.
             | Rust will do unneeded check in those cases.
             | 
             | I do agree though, the cost of the check is so small that
             | it doesn't matter in most cases.
        
           | Georgelemental wrote:
           | Rust also gives you choices. If, for whatever reason, you
           | _need_ to get rid of that runtime check, you can always use
           | `unsafe`. Safety is the happy path, but you aren 't locked
           | in.
        
           | xiphias2 wrote:
           | Dropping to unsafe rust is quite easy as long as you
           | understand what you're doing (and reading pointer is
           | generally unsafe).
           | 
           | In the case of automatically upgrading from int to bignum, or
           | green threads, going lower level would be much harder.
           | 
           | I just wish Rust stayed focused on being the best systems
           | level programming language (for example finishing the SIMD
           | package, getting into stable Rust, getting language level GPU
           | integration similar to CUDA / OpenCL).
           | 
           | PyToch, and now the newer GGML for example is still written
           | in C++ for example
           | 
           | I still like the changes being made in the language, just not
           | the focus.
        
       | ch33zer wrote:
       | What a wonderfully self aware and honest piece of writing.
        
       | FrustratedMonky wrote:
       | Great read from a creator of a language. There are so many new
       | languages these days, it is always enlightening to hear the
       | author discuss design tradeoff's. That being said, man, its easy
       | to forget how difficult and complicated creating a good language
       | can be.
       | 
       | Makes me wonder if things like Linux, or C++, were historical
       | anomalies, the stars aligned. How many good projects fail because
       | of 'loosing arguments' that should have been won, or the
       | community didn't form, etc... a million things..
        
       | weinzierl wrote:
       | One thing I wished was on this list, but wasn't, is syntax. I
       | love many syntax decisions Rust made, but I wish Rust hasn't
       | borrowed so much syntax from C/C++. The syntax of these languages
       | was designed under (for todays standards) weird keyboard and
       | encoding constraints and many choices are just odd.
       | 
       | To give you a few examples:
       | 
       | - = instead of == for equality would have been the natural choice
       | 
       | - := for assignment is similar enough to what is used in math for
       | definition, so that languages like Pascal use it
       | 
       | - <> for inequality is something SQL got right
       | 
       | Smaller things that bug me are the ubiquity of the double colon
       | (::) and the weird mixture of snake case and camel case
       | conventions.
       | 
       | And not to leave the wrong impression, I think Rust got many
       | things very right. My personal highlights are:
       | 
       | - -> for the return value
       | 
       | - concise keywords like `fn`
       | 
       | - `where` for constraints
       | 
       | In general more Algol/Pascal and Haskell - less BCPL and C/C++.
        
         | pitaj wrote:
         | Assignment is more common than equality. Typing two identical
         | sequential characters "==" is much easier than typing ":="
         | especially since on most keywords colon requires shift and
         | equal does not. And having a two-character equality operator
         | helps align things with "!=", ">=", "<=".
        
           | weinzierl wrote:
           | Equality is so much more common than the other relational
           | operators that it justifies the single letter.
        
         | jstx1 wrote:
         | Syntax is mentioned in the article - it's under "Complex
         | Grammar".
        
         | epage wrote:
         | > - := for assignment is similar enough to what is used in math
         | for definition, so that languages like Pascal use it
         | 
         | I think its cppfront that is taking the approach of `:=` being
         | a declaration with the type being inferred (ie shorthand for `:
         | Type =`). Reading up on that has made me the most ok with
         | applying this to functions (which I see coming up more these
         | days) but I think i still prefer functions having a more
         | distinct look as I process them differently when reading. Now,
         | cppfront's approach to types I think is bonkers, making
         | critical details hard to find except maybe through convention.
         | 
         | https://github.com/hsutter/cppfront
         | 
         | > <> for inequality is something SQL got right
         | 
         | Maybe I'm not recognizing the biases of my own learning
         | background but this never reads right to me vs "not equal" /
         | `!=`.
         | 
         | > - concise keywords like `fn`
         | 
         | In other discussions, it sounded like Graydon had an upper
         | limit of 4 characters for keywords
         | 
         | https://www.reddit.com/r/rust/comments/13oemrg/question_abou...
         | 
         | For me, I had a "whoosh" moment for `fn` and always thought it
         | a weird abbreviation, completely overlooking "fn" keys on
         | laptops.
        
           | weinzierl wrote:
           | My very first programming language had fn, so maybe my love
           | for it is just a form of nostalgia.
        
       | junon wrote:
       | > I was weirdly focused on [the Actor] model that in practice has
       | many issues
       | 
       | I maintain the actor model is probably the most theoretically
       | perfect concurrency and distributed computing model. The holy
       | grail. We just don't have the right hardware for it and it's
       | extremely limited by addressability issues with current
       | technology.
       | 
       | So I don't really find this surprising, nor disagreeable. It's
       | just not a model that Works Well at present.
        
         | derefr wrote:
         | I'm not sure I agree. Runtimes like Erlang's work great for
         | many things.
         | 
         | You just have to be aware that actors aren't going to magically
         | get you more CPU cores -- i.e. you can have as many IO-bound
         | actors as you want; but a CPU-bound actor (done correctly, such
         | that "gets out of the way" of actor scheduling) is just a
         | regular CPU-bound preemptive OS thread; and you can only
         | _realistically_ have as many of those as you have CPU cores in
         | your machine, before you start experiencing highly degraded
         | performance.
         | 
         | Most systems don't _need_ more than 100 (different) CPU-
         | saturating things to happen at a time. If you do, actors won 't
         | save you... but nothing else will, either. You'll need to scale
         | horizontally. (At which point, the actor _model_ becomes very
         | useful from another perspective -- that of transparent
         | distribution of messages between nodes.)
         | 
         | But tbh, there's a reason that, even on the systems Erlang was
         | originally designed for and is "idiomatic" for -- those being
         | telecom packet switches -- the Erlang software only performed
         | the role of the _control plane_. There was also a _data plane_
         | in each of those boxes -- some kind of FPGA or ASIC -- designed
         | specifically for the job of applying a list of active routing
         | rules at each input port, such that packets on that port would
         | be unwrapped, maybe filtered, route-matched to an output port,
         | maybe buffered to combine with other packets, then re-wrapped
         | and emitted at said output port. The job of the Erlang code was
         | to listen for _signals_ bubbled up from the data plane, in
         | order to build complex state-machines, do accounting, etc.,
         | resulting in _change commands_ being pushed back down _into_
         | the data plane ruleset.
         | 
         | Actors are great for keeping a bit of local state in order to
         | make decisions, and coordinating with other actors and _their_
         | local state to make more complex decisions.
         | 
         | Actors implemented naively -- where all actors are uniformly
         | green threads -- are not so great for doing the same thing over
         | and over, at scale. Telling N actors to do the same thing is no
         | substitute for a DSP, nor for a tensor core.
         | 
         | But this isn't an indictment of the actor model, nor of current
         | hardware, but rather of the current state-of-the-art in actor-
         | model languages. You could totally create an actor-model
         | programming language where actor-pools that are doing SIMD are
         | transparently "promoted" during compilation into GPU shaders /
         | FPGA gate-networks / etc. Nobody's done it, but there's nothing
         | stopping anyone. (Anyone interested in trying this could
         | probably do a proof-of-concept on top of Elixir's Nx library.)
        
       | erik_seaberg wrote:
       | I'm very glad expressivity won out. I would like our profession
       | to stop accepting tools that waste effort.
       | 
       | I've always seen safety and lifetimes and borrowing as the main
       | value prop, so I was surprised to see he was sort of aiming at an
       | ML without GC, rather than a C++ that doesn't blow up.
        
         | dan-robertson wrote:
         | Early rust did have gc so it was more of an ML with optional
         | gc, and maybe a dot operator. (And no closures apparently)
        
       | Aardwolf wrote:
       | > Tail calls. I actually wanted them! I think they're great. And
       | I got argued into not having them because the project in general
       | got argued into the position of "compete to win with C++ on
       | performance" and so I wound up writing a sad post rejecting them
       | 
       | I don't understand the subtleties here: Is tail calls an
       | optimization the compiler can do irrespective of the language? Or
       | is there something that prevents this and _requires_ the compiler
       | to use stack here? Is there any visible effect to the programmer
       | from supporting tail call or not, other than performance and
       | stack depth?
       | 
       | How does not supporting them compete with C++ performance?
        
         | [deleted]
        
         | layer8 wrote:
         | It's about stack size. Programs that rely on tail calls (in
         | particular recursive ones) may cause the stack to overflow when
         | the language implementation doesn't actually support tail
         | calls, for example when using tail calls to recursively process
         | a list that is larger than (some constant fraction of) the
         | stack. With an infinite stack, it would just be an
         | optimization, but in practice the stack is finite (and
         | significantly smaller than the heap), and thus doesn't lend
         | itself to such a programming style when the implementation
         | doesn't support tail call optimization.
        
           | pmontra wrote:
           | That's why we write programs that rely on tail calls only for
           | languages that have TCO in their specification. Where TCO is
           | idiomatic (e.g. Elixir) implementations not supporting it are
           | impossible because they will be able to run only the simplest
           | programs.
           | 
           | The problem is adding TCO to a language that was born without
           | it. It won't break existing programs but you'll be able to
           | run a program using TCO only with a specific compiler or
           | interpreter from a given version. It could catch up if the
           | main implementation of the language does it, but often we
           | care about compatibility with the alternative
           | implementations. Think about Ruby and Python, MRI and CPython
           | and all the other implementations of the language. If I'm not
           | wrong, MRI added TCO under a runtime configuration variable
           | but JRuby doesn't support it.
        
           | tinco wrote:
           | I thought on 64 bit systems the stack could basically be
           | infinite for all practical purposes. Is this only a problem
           | on more limited (i.e. <64bit) systems or am I misremembering
           | when I last learned about stacks 10+ years ago?
        
             | aardvark179 wrote:
             | It is always a problem, in the same way that holding on to
             | huge objects is always a problem. Let's think about this in
             | terms of a memory map, so ignoring limits of physical
             | memory. On 64bit systems we actually have 48bits of usable
             | address space, so that's 256TB. Now, the OS is going to
             | need some workspace, but I think we can ignore that for
             | this argument. So if you're only going to run 4 threads
             | then each one could have a huge stack of 32TB, but if we
             | wanted to run a huge server with a thread per connection
             | then maybe we can only have 256MB per thread.
             | 
             | Now, 256MB is pretty huge, but we probably want way more
             | memory kept for heap storage and similar things (because
             | managing lifetimes purely on the stack will be hard and
             | might require a lot of copying of data), so it will be less
             | than that. Now, you may ask, "Why not start with small
             | stacks and make them bigger as needed?" It's a good
             | question, but since our stacks are contiguous areas of
             | memory and we don't know what's in them, we still have to
             | space them out in our memory map allowing for as much
             | growth as is needed.
             | 
             | We might solve some of these problems by introducing
             | segmented stacks, but this is one of those problems that
             | crosses so many language, runtime, and OS boundaries that
             | it's been hard to do in the general case, and it feels like
             | gently nudging people towards writing code that can be
             | tail-optimised is easier, just as it's easier to push
             | people to use async than it is to provide systems and APIs
             | that would allow for blocking code and a huge number of
             | threads.
        
               | tinco wrote:
               | Thanks, I didn't realise the usable address space was so
               | "small", those numbers are well within reasonable usage
               | so that's definitely too limiting to just say it's
               | infinite and not worry about it.
        
               | layer8 wrote:
               | Apart from address space, there's also simply the
               | question of wasted resources, because in tail-call-
               | optimizable programs, that stack space is by definition
               | not strictly needed (otherwise it couldn't be optimized
               | away), so you might unnecessarily be wasting many GBs of
               | memory. Lastly, TCO can improve cache locality and thus
               | potentially speed up the program.
        
             | Merad wrote:
             | Yes and no. In theory it would be totally fine to
             | "allocate" a stack that was exobytes in size (or whatever
             | extremely large number). Thanks to virtual memory no one
             | really cares... if that memory isn't actually being used.
             | But as your stack grows larger and larger the OS has to
             | find pages of physical memory to assign to that allocation,
             | and you're simply going to run out of memory. A high end
             | home PC or average server is only going to have 64-128 GB
             | of RAM, a very high end server might have a few TB. Of
             | course when you start to run out of physical memory the OS
             | will start paging (dumping data from RAM to disk to free up
             | memory), but that will kill the performance of your app
             | even with high end SSD's. If you keep chugging along with
             | your stack growth you'll probably hit OS limits on page
             | file size next, if not you'll eventually run out of disk
             | space.
        
           | Aardwolf wrote:
           | What I actually meant to ask is: given what you said, is
           | supporting it then even a _language_ feature, and not a
           | _compiler_ optimization instead?
           | 
           | If rust says they don't support it, does it mean they don't
           | even allow the compiler to do it?
           | 
           | Obviously the syntax of the language itself already supports
           | calling the function itself, and whether tail call
           | optimization is supported or not doesn't affect the visible
           | result afaik (except in case of stack overflows /
           | performance)
        
             | bluGill wrote:
             | Its not that the compiler cannot do it, it is that you
             | cannot be sure the compiler will.
             | 
             | Some constructs cannot be optimized for tail recursion.
             | Languages with tail calls have guidelines of how to must
             | write your code to ensure the tail call happens - often a
             | seemingly small code change is the difference between tail
             | call optimization happening or blowing up the stack.
             | 
             | Second, in at least some cases where the optimizer can
             | apply tail calls you need to be guaranteed it will happen.
             | Nothing stops a C++ optimizer from applying tail call
             | optimization (I don't know if any do, but it is allowed in
             | some cases), but the language doesn't require it, so even
             | if your optimizer supports it you can never know that it
             | happens - and more importantly you cannot be sure that
             | after changing the code or upgrading your compiler you will
             | still get it. Thus even if your optimizer supports tail
             | code optimization you dare not do deep recursion.
             | 
             | If your language doesn't have support for tail calls you
             | cannot do deep recursion with confidence. If your language
             | does, then you can do deep recursion so long as you follow
             | the rules of the language. (whatever those are - I'm not up
             | on the latest research here, so I don't know the state of
             | modern tail recursive languages are.)
        
             | layer8 wrote:
             | It's a language feature in the sense that it determines
             | which programs are viable in the language. This is similar
             | to garbage collection. In principle, you never have to
             | explicitly free memory. Given infinite memory, garbage
             | collection would only be an optimization. But since memory
             | is finite, it becomes a language feature. Without garbage
             | collection, programs that don't free their unused memory
             | will typically run out of memory sooner or later, similarly
             | to how a tail-calling program will run out of stack space
             | if the language doesn't support it.
        
             | Tyr42 wrote:
             | I suppose a difference of speed turns into a difference of
             | ability at some point. If you can't rely on tail calls, you
             | will have to manually trampoline the calls, just in case
             | the optimization didn't kick in and your program dies.
             | 
             | Or like xmtp or mail or whatever, you generally write your
             | client assuming you are going to talk to any spec
             | compliment server, which can stop you from using some
             | extensions if you aren't sure they will be supported.
             | 
             | Possible is not the same as guaranteed.
        
         | JonChesterfield wrote:
         | Tail call optimisation means no work can be done after the call
         | and before the return. You can't deallocate stack, call
         | destructors, convert types, rethrow exceptions.
         | 
         | Therefore _yes_ the compiler can always do it, but it may
         | involve patching the called function to do some work. Splicing
         | code into it and/or changing the calling convention. That's
         | difficult to do for unknown caller/caller pairs, e.g. function
         | pointers or separate compilation to machine code without enough
         | metadata to patch it later. It runs a bit close to
         | "sufficiently smart" compiler which usually means doable in
         | theory but unlikely in practice.
         | 
         | If you pick global designs to make them easier - probably most
         | notably having the callee clean up the stack frame from the
         | caller - and that makes other things slower, then you've given
         | the competition an edge.
         | 
         | Clang now has tail call annotations for C++, which works mostly
         | because the compiler can reject them in the cases where it
         | hasn't implemented the lowering. Rust can probably have it in
         | the same approximate circumstances as C++.
        
           | phi-go wrote:
           | Interesting that tail calls are mentioned and implied to
           | never come to Rust. I agree with your assessment and actually
           | there is a RFC[1] in the works to support them. (Note that
           | while I'm the author, this RFC is definitely a community
           | effort.)
           | 
           | [1] https://github.com/rust-lang/rfcs/pull/3407
        
       | mhd wrote:
       | Ah, Sather gets mentioned. That's a name I haven't heard in a
       | long time. I remember looking at it during the latter half of the
       | 90s when I was searching for the perfect OO language...
       | 
       | https://www1.icsi.berkeley.edu/~sather/
        
       | TowerTall wrote:
       | The article starts with "In a recent podcast about Rust
       | leadership, the BDFL question came up again"
       | 
       | What it BDFL?
        
         | Fiahil wrote:
         | Benevolent dictator for life.
         | 
         | "Benevolent dictator for life (BDFL) is a title given to a
         | small number of open-source software development leaders,
         | typically project founders who retain the final say in disputes
         | or arguments within the community. The phrase originated in
         | 1995 with reference to Guido van Rossum, creator of the Python
         | programming language."
        
           | dochtman wrote:
           | Of course Guido ended up not completing the "For Life" part
           | -- neither his life nor the life of the language. (I think
           | PEP 8016 is the one that started the steering council to take
           | the place of the BDFL? https://peps.python.org/pep-8016/)
        
           | TowerTall wrote:
           | Interesting. Isn't a BDFL needed in Open Source projects (not
           | talking specific about Rust, but Open Source in general)? I
           | work in a commercial software company. We have a CTO and he
           | retain the final say in disputes or arguments. He also steers
           | the global technical direction we take the software and has
           | the final say, but he can get fired. Is that the main
           | difference between a CTO and a BDFL?
           | 
           | What alternatives are there?
        
             | sanxiyn wrote:
             | The usual alternative is a committee, see for example
             | Apache Software Foundation's Project Management Committee
             | Guide: https://www.apache.org/dev/pmc.html
        
               | pm215 wrote:
               | I suspect an informal grouping (i.e. more long-
               | standing/senior contributors with default control over
               | some parts of the codebase and operating by informal
               | consensus between themselves) is probably at least as
               | common (maybe more so) than an officially named committee
               | with rules of procedure.
        
             | zozbot234 wrote:
             | The main difference between a CTO and a BDFL is that if you
             | disagree with a BDFL, you can just fork the project under a
             | new name. This right to fork is key to making FLOSS
             | development work. If you disagree with a CTO, there isn't
             | much that you can do besides buying up the whole company
             | (see Elon Musk and TWTR as an example).
        
             | emanuele232 wrote:
             | The most inefficient decision-making structure known,
             | democracy and voting?
        
         | compressedgas wrote:
         | https://en.wikipedia.org/wiki/Benevolent_dictator_for_life
        
         | kjandersen wrote:
         | Benevolent Dictator for Life, a governance model quite
         | prominent in PL and OSS at large.
        
         | [deleted]
        
         | lhrvn wrote:
         | A euphemism for a software project dictator where the moral
         | superiority is baked into the title. The dictator does not have
         | to be benevolent in practice.
        
       | vintagedave wrote:
       | > The other option, which I wanted, was for these to be compiler
       | builtins open-coded at the sites of use, rather than library-
       | provided. No "user code" at all (not even stdlib). This is how
       | they were originally in Rust: vec and str operations were all
       | emitted by special-case code in the compiler.
       | 
       | One of the things I like about Delphi is that it has some
       | powerful types as compiler intrinsics. Sets, strings (the
       | compiler-generated code does call into RTL methods for things
       | like finding substrings, but the string type itself), and so
       | forth are all compiler-generated.
       | 
       | I would like to see more, in fact: I think a map type would be a
       | great inbuilt addition. (What I'd really like is compiler stubs
       | so you could link in your own implementation. Whatever is linked
       | in, it's then heavily optimised by the linker to be inlined etc
       | as appropriate.)
        
       | tormeh wrote:
       | I think Rust is somewhat cumbersome as a language, but I also
       | think the right tradeoffs were made. There are far fewer and far
       | worse competitors in the high-performance space than in the
       | general purpose space.
        
       | mcguire wrote:
       | " _Exterior iteration. Iteration used to be by stack / non-
       | escaping coroutines, which we also called "interior" iteration,
       | as opposed to "exterior" iteration by pointer-like things that
       | live in variables you advance. Such coroutines are now finally
       | supported by LLVM (they weren't at the time) and are actually a
       | fairly old and reliable mechanism for a linking-friendly, not-
       | having-to-inline-tons-of-library-code abstraction for iteration.
       | They're in, like, BLISS and Modula-2 and such. Really normal
       | thing to have, early Rust had them, and they got ripped out for a
       | bunch of reasons that, again, mostly just form "an argument I
       | lost" rather than anything I disagree with today. I wish Rust
       | still had them. Maybe someday it will!_"
       | 
       | I remember that one. The change was shortly after I started
       | fooling with Rust and was _major._ Major as in it broke all the
       | code that I 'd written to that point.
       | 
       | " _Async /await. I wanted a standard green-thread runtime with
       | growable stacks -- essentially just "coroutines that escape, when
       | you need them too"._"
       | 
       | I remember that one, too; it was one of the things that drew me
       | to the language---I was imagining something more like Pony
       | (https://www.ponylang.io/).
       | 
       | " _The Rust I Wanted probably had no future, or at least not one
       | anywhere near as good as The Rust We Got._ "
       | 
       | Almost certainly true. But The Rust We Got is A Better C++, which
       | was never appealing to me because I never liked C++ anyway.
        
       | ZephyrBlu wrote:
       | This is an interesting read, because I read it as "I would have
       | done all these things which would have kept the language more
       | pure to my vision but less accessible".
       | 
       | I also found this bit interesting: "It's easier to work with than
       | C++, but that's fairly faint praise".
       | 
       | I see this kind of thing in my own personal projects all the
       | time. I'm thinking "oh it would be really cool if I built X" when
       | in reality most of the time users just want really simple stuff.
       | 
       | Being easier to work in than C++ might be faint praise, but it's
       | probably the biggest draw of Rust for me. I don't want to touch
       | C++ with a 10ft pole, but I love using Rust.
        
         | truculent wrote:
         | I'm not sure it would be less accessible. I think the trade-
         | offs would lean towards simplicity, and perhaps less
         | familiarity. But with the right mental models, I think this
         | could enhance accessibility (less "magic", or strange edge-
         | cases). For example, see the discussion on not minding if users
         | have to write something in a more verbose way if it preserves
         | the language's principles. This type of trade-off is something
         | that lowers the barrier to entry for beginners, who don't mind
         | writing things out the long way.
        
           | ZephyrBlu wrote:
           | I'm using accessible in a very general sense. The sense of
           | "how niche would this be". My impression is that Rust would
           | have been a very niche language if Graydon was the BDFL.
        
         | masklinn wrote:
         | Note that the quote is specifically about parsing Rust syntax,
         | it's not about the language in general (which is a _lot_ easier
         | and safer to work in than C++).
        
           | ZephyrBlu wrote:
           | True, but I feel like the sentiment generalizes very well.
           | I'm sure that C++ is perceived to have a similarly low bar in
           | many areas.
        
       | neonsunset wrote:
       | I can't imagine Rust being even remotely viable without having
       | generics or using LLVM to target a wide variety of platforms with
       | sufficiently good codegen quality.
        
       | krupan wrote:
       | The key challenge of rust for me:
       | 
       | "Complex grammar. I've become somewhat infamous about wanting to
       | keep the language LL(1) but the fact is that today one can't
       | parse Rust very easily, much less pretty-print (thus auto-format)
       | it, and this is an actual (and fairly frequent) source of
       | problems. It's easier to work with than C++, but that's fairly
       | faint praise. I lost almost every argument about this, from the
       | angle brackets for type parameters to the pattern-binding
       | ambiguity to the semicolon and brace rules to ... ugh I don't
       | even want to get into it. The grammar is not what I wanted.
       | Sorry."
        
         | kzrdude wrote:
         | Is there a practical issue with this, or is this more
         | philosophical?
        
           | cryptonector wrote:
           | Is there anything easier to parse than Lisp? Plus you get
           | homoiconicity and `quote` is free. But... it's ugly.
        
       | Lk7Of3vfJS2n wrote:
       | Has Rust gone beyond the point of no return? Have less elegant
       | features possibly been embedded in the language that have put
       | Rust on the track to never become the hypothetically perfect or
       | unblemished language?
        
         | steveklabnik wrote:
         | The answer to your second question is "yes" for every single
         | language that's ever been made or will be made.
        
       | Decabytes wrote:
       | I know we haven't gotten to that point yet, but I'm curious to
       | see if in my lifetime we get a successor to Rust that fixes a lot
       | of issues people have with the language now.
        
       | anonyfox wrote:
       | I love Rust and built some production code with it in the past.
       | But nowadays I want something more simple so that not-so-senior
       | developers can pick it up quickly, and I want flawless tooling,
       | and willing to sacrifice a bit of performance. So basically I
       | often end up with Go. Go is exceptionally great in tooling,
       | ecosystem, any objective metrics like build times or
       | crosscompilation... but I still don't like the language itself
       | personally.
       | 
       | If there would be something just like Go, but with a bit more
       | powerful typesystem like Rust has (Option<T> instead of `err !=
       | nil`, and so on), and a simplified ML-like language instead of an
       | imperative one... that would be my dream.
        
         | gardaani wrote:
         | Rust borrow checker and lifetimes are sometimes so frustrating.
         | I hope that the Jakt language will evolve to the language I'd
         | like to see: Swift without ties to Apple and Objective-C.
        
           | bscphil wrote:
           | Apple has been pushing Swift on Linux enough that I've been
           | checking out its progress every so often. It will probably
           | never get entirely free of ObjC but it's a nice enough
           | language that I'd really like to be able to use it for cross-
           | platform desktop applications. Having a pretty robust
           | standard library and a stable ABI (currently just on macOS)
           | are big plusses, in my book. Having a built in REPL is icing
           | (delicious, delicious icing).
        
         | amelius wrote:
         | OCaml?
        
           | anonyfox wrote:
           | really tried to get into it for a while, but the tooling
           | situation feels like the polar opposite of Go, where
           | everything needed in daily work is right there in a
           | standardized way and just works out of the box.
           | 
           | I kinda like the language (its what I want basically) but the
           | operational aspects are what I actually need and want first
           | and foremost.
        
         | scythe wrote:
         | >like Rust has (Option<T> instead of `err != nil`
         | 
         | Didn't they implement generics already?
         | 
         | https://go.dev/blog/why-generics
         | 
         | It seems like their implementation has enough power to do this
         | in most cases, although "Option" is not built in.
        
         | kaba0 wrote:
         | Scala, Haskell, Java, Kotlin.
        
         | [deleted]
        
         | netbioserror wrote:
         | Nim gets real close. It doesn't have great UI tooling, but its
         | command line tools are quite good.
        
           | xhevahir wrote:
           | Nim is quite imperative, too. And its BDFL(Araq) takes a dim
           | view of functional programming: https://forum.nim-
           | lang.org/t/8927
        
           | rdevsrex wrote:
           | Yeah I've been looking into Nim and I want to build something
           | substantial in it and see how it performs and feels to write.
        
         | YorickPeterse wrote:
         | Perhaps you'd be interested in Inko (https://inko-lang.org/).
         | It's obviously not there yet in terms of tooling and what not,
         | but it might scratch an itch for those looking for something a
         | bit like Rust, but easier to use.
         | 
         | Disclaimer: I'm the author of said language :)
        
           | justinpombrio wrote:
           | Lots of languages are aiming for "like Rust, but easier to
           | use" -- I think I could name half a dozen. It's a laudable
           | goal!
           | 
           | I'm curious how references in your language work. I see the
           | very small example, but it doesn't explain much.
           | 
           | Some questions in that regard: Is `&T` a type? Can you store
           | it in a structure, or return it? Can you have a reference to
           | a reference? If you can have a function `f(&T, &T) -> &T`,
           | how do you distinguish whether the reference it returns lives
           | as long as the first or second argument? If references can't
           | be stored in structs, how do you do non-owned iterators, or
           | string slices?
        
         | jeltz wrote:
         | Yeah, that is exactly what I want for when I build web
         | applications. Something with an ecosystem like Rust's, with
         | more advanced type system than go but which sacrifices some
         | performance for ease of development.
        
           | californical wrote:
           | I feel like Crystal is close, they just need more community
           | to build out their ecosystem. Otherwise it seems like a
           | perfect language (fun, simple to understand, compiled + fast,
           | has types)
        
             | anonyfox wrote:
             | Already played with it, what has killed me are the
             | outragously long compile times for nontrivial code and no
             | "easy" way to crosscompile binaries, besides from some
             | docker hack. Also its like a modern Ruby, which is great in
             | general, but I really want ML-like functional style
             | programming instead of OOP
        
         | weatherlight wrote:
         | I wish someone would go back and give love to Standard ML. it
         | such a great language, small, and easy to learn, extremely
         | fast, compiles fast, and expressive to boot.
         | 
         | It's tooling isn't great though. I think its syntax is a lot
         | nicer than OCaml's for example.
         | 
         | There is this wonderful language that is up and coming called
         | Roc that look promising.
         | 
         | https://www.youtube.com/watch?v=6qzWm_eoUXM
         | 
         | language examples here -> https://www.roc-lang.org/tutorial
        
         | jjtheblunt wrote:
         | Have you dismissed F# ?
        
           | hajile wrote:
           | The rest of the world has. If you're going to use .net,
           | there's almost zero chance that you'll get permission to use
           | F#.
           | 
           | For whatever reason, C# devs seem incredibly resistant to
           | even just looking at F#.
        
             | pjmlp wrote:
             | Do you want reasons? Here are a couple from someone that
             | has learned ML with Caml Light, and most of the latter ML
             | variants.
             | 
             | VS Tooling lacking versus C#/VB, no support for code
             | generators, no support for Roslyn, no support for GUI
             | frameworks, no support for EF tooling, many .NET vendors
             | don't support projects if using F#, community likes to
             | create their own wrappers instead of embracing standard
             | .NET projects, ....
        
           | FrustratedMonky wrote:
           | I would like to second F#. It seems to have a lot of what
           | people want, so why isn't it more popular?
        
             | anonyfox wrote:
             | Can I natively build self-contained binaries? One of Go's
             | biggest advantages is the delivery chain from code to
             | server (build for target arch, copy to target, ./run)?
        
               | naraga wrote:
               | I believe you can https://learn.microsoft.com/en-
               | us/dotnet/core/deploying/sing...
        
               | throwaway2037 wrote:
               | I can vouch for it! I returned to C# for a tiny desktop
               | GUI recently. I was blown away by how simple is the
               | tooling to create a single monolithic binary. It's great
               | for enterprise distribution. "No more installer."
        
               | jjtheblunt wrote:
               | Yes
        
               | ontologiae wrote:
               | I had to deal with it, when I hadto parse Transac-SQL
               | code, and it's not handy. Very difficult to have a single
               | executable file containing everything, it needs a whole
               | subtree. OCaml is easier with Opam and ocamlbuild tools.
        
           | zidad wrote:
           | Unfortunately a lot of people will dismiss C# and F# just
           | because it has the Microsoft label. I do think they're
           | missing out.
        
             | anonyfox wrote:
             | Nowadays I program in a mostly Microsoft world (VSCode,
             | Github, npm, typescript, GPT, ...) and I think its quite
             | good, so no bias here. But the "shipping a native binary
             | straight to the server that just works" is absolutely
             | crucial for me
        
               | walljm wrote:
               | .net will let you do that now. You can ship precompiled
               | binaries with Native AOT.
               | (https://learn.microsoft.com/en-
               | us/dotnet/core/deploying/nati...)
        
               | [deleted]
        
               | vips7L wrote:
               | And even before native AOT you could use publish single
               | and have a single binary.
        
               | mmargerum wrote:
               | ASP.NET AOT support coming in .net 8
        
         | zidad wrote:
         | You could try C# (or Kotlin if you have a MS bias ;-)). Both
         | really nice general purpose languages, quite performant despite
         | having a GC and great available tooling.
        
           | anonyfox wrote:
           | I think I still would have to deal with a lot of OOP and
           | imperative code there, C# feels like a kitchen sink of stuff
           | - right?
        
             | [deleted]
        
             | zamalek wrote:
             | C# is horribly OOP. I use it in my day job, and the
             | frameworks wield OOP overcomplexity proudly. It has likely
             | gone beyond Java as the posterchild for OOP: interfaces
             | that are implemented once, classes that are instantiated
             | once. You can't get away from it either, it is
             | _practically_ part of the stdlib and everyone cargocults
             | it.
             | 
             | Rust is imperative too, though.
        
               | hadrien01 wrote:
               | The language doesn't push you towards interfaces
               | implemented once, but many developers indeed persist
               | doing it for no reason at all. With proper code review
               | we're able to make that practice go away on the projects
               | I'm working on.
        
               | vips7L wrote:
               | Isn't this just mostly an issue with mock testing in C#?
               | Developers cargo cult single implementation interfaces
               | because its so hard to mock concrete classes.
        
               | rjbwork wrote:
               | >Isn't this just mostly an issue with mock testing in C#
               | 
               | Yes. You have two choices: Interface implemented once, or
               | virtual on all your public members.
               | 
               | I personally think Interface is the sane choice.
               | 
               | Would be nice if the .NET devs let us mock POCO's
               | though...
        
               | hadrien01 wrote:
               | You can mock (well, "fake") POCOs with packages like
               | AutoFixture that use reflection to generate mostly fake
               | data, depending on what you need. You don't need
               | interfaces or virtual, but you do need public getters
               | unfortunately.
        
               | za3faran wrote:
               | I've seen the same in golang, which ironically is
               | supposed to be vehemently against OOP.
        
               | diarrhea wrote:
               | Lots (all?) major frameworks push for dependency
               | injection though, where interfaces are a must, as far as
               | I know (not for DI-the-principle, but DI-as-implemented).
               | ASP.Net Core is a good example. It's not C# at the
               | language level _forcing_ interface-driven development,
               | but frameworks like ASP.Net are so tightly integrated
               | that boundaries blur.
        
               | zamalek wrote:
               | Yup, that's what I meant by _" practically."_ Nobody
               | forces you to use the Microsoft.x nuget packages, but
               | good luck at finding packages that don't rely on
               | Microsoft.DI, or Microsoft.Hosting, or what-have-you.
        
               | hadrien01 wrote:
               | You don't need interfaces for dependency injection, you
               | can inject classes directly. I see interfaces more for
               | separation of concerns (with domain-driven design pushed
               | from all parts in the .NET world at the moment...), but
               | that's not an obligation.
        
             | chronofar wrote:
             | You can avoid quite a bit of OOP with Kotlin if you like,
             | and with Arrow make some really nice functional code. Imo
             | it offers a great balance of imperative vs functional,
             | providing powerful tools for whatever style suits the
             | project best.
        
         | avgcorrection wrote:
         | > If there would be something just like Go, but with a bit more
         | powerful typesystem like Rust has (Option<T> instead of `err !=
         | nil`, and so on), and a simplified ML-like language instead of
         | an imperative one... that would be my dream.
         | 
         | Not to be snarky (for once) but
         | 
         | - More powerful type system: that goes against the whole
         | implementation culture behind Go and their wider philosophy
         | 
         | - ML-inspired instead of imperative: goes _even more_ counter
         | to the above
         | 
         | I have never tried Go and I will probably never care to try it,
         | but I have never seen a language which manages to both be (1)
         | simple in the Go-sense and (2) look remotely anything like an
         | ML language.
        
       | pjmlp wrote:
       | While it has a been a great piece of wrong to introduce affine
       | type systems into mainstream, it hardly justifies outside domains
       | where any kind of automatic memory allocation isn't either a
       | blocker, or religious issue that won't be sorted out even by
       | proving the contrary.
       | 
       | I see the ongoing attempts to add linear types for low level
       | coding, alongside automatic resource management more future
       | proof.
        
       | truculent wrote:
       | It sounds like the Rust He Wanted has a lot of thematic
       | similarities to Elm. Interestingly, Elm has a BDFL, and a
       | development process that reflects that. And he's right - there
       | are a lot of people who really don't like that!
       | 
       | Overall, a really interesting article. Though I like today's
       | Rust, I do think I would prefer the trade-offs made by the alt-
       | Rust outlined here.
       | 
       | Perhaps it's just my own personal preference, but I think there
       | is a strong bias in users towards what they are already familiar
       | with, and it's hard to break away from those without a BDFl or
       | similar position of authority who can impose their vision.
        
         | EdwardDiego wrote:
         | My understanding is that for the Elm BDFL, the B was dropped.
        
           | truculent wrote:
           | The language creator has preferred to work on the development
           | of the language largely in private, for the last couple of
           | years. He's explained why he's chosen to do this and why he
           | thinks it is beneficial. But, understandably, some people
           | haven't take too well to the lack of updates.
           | 
           | I think whether you view that as benevolent or not (I would,
           | because I agree with lots of de facto development standards
           | being detrimental to language quality, but I understand that
           | that is my opinion and not hard fact) is in the eye of the
           | beholder.
        
             | EdwardDiego wrote:
             | I was more thinking of a) the ring-fencing of native code
             | and b) his conduct in discussions about such things. Guido
             | was never that abrasive.
        
       | mjburgess wrote:
       | Over the evolution of Rust, I've been increasingly despairing
       | about many of the things Graydon here dislikes. I assumed the
       | present "syntactical insanity" was, somehow, intended; it seems,
       | really, it wasn't.
       | 
       | I find Rust basically unusable -- at the level of abstraction I
       | want to write code, basic definitions break line limits.
       | 
       | Rust seems to be a repetition of C++'s mistake: a language which
       | conspires you to pretend it's another. There are now nearly as
       | many Rusts as C++s.
       | 
       | If I return to any domains where Rust would be relevant, I'd
       | probably now opt for Zig or equivalent.
        
         | laeri wrote:
         | Can you give an example where Rust is unusable? I have been
         | getting into Rust recently and it feels very natural after
         | getting accustomed to dealing with `Result` and `Option`. And
         | what do you mean about "a langauge which conspires you to
         | pretend it's another"?
        
         | kaba0 wrote:
         | Rust is a low-level programming language meant to control
         | everything about the code's execution. It will by almost
         | definition, be complex.
         | 
         | You seem to want contradictory things -- if you don't need that
         | level of control just use any of the litany of high level
         | languages with nice syntaxes. I don't think we can eat this
         | cake anytime.
        
           | marcosdumay wrote:
           | Yeah... Rust was first intended to be a high-level language
           | where one could create an entire browser engine without
           | sacrificing a lot of performance.
           | 
           | The fact that it is a law-level language best fit for system
           | programing came much later. Late enough that there are people
           | here and there still trying to use it for the original use-
           | cases.
        
           | mjburgess wrote:
           | The major innovation in this space is to make the compiler an
           | interpreter which can evaluate the language at compile time
           | (and emits code for runtime where it cannot).
           | 
           | This makes brining in the "full power of dynamic languages"
           | almost trivial and extremely performant.
           | 
           | Delete half of rust's symbols, get rid of its macro system,
           | rewrite it until lifetimes are inferred /or/ allocators are
           | explicitly chosen, etc. etc.
           | 
           | I don't want to feel visually assaulted when writing the type
           | signature of the sort of function common in a dynamic
           | language. Here `mypy` is also treasonously guilty.
           | 
           | Consider, eg., zigs "function which _returns_ a type at
           | compile-time " is an example of where blindingly-obvious
           | syntax retains its blindingly-obviousness because of compile-
           | time eval... ie., we dont need a "second syntax" to program
           | the compiler.
           | 
           | This "two syntaxes, one for the runtime and one for the
           | compiler" approach -- has swamped Rust as it aims for greater
           | expressiveness. Not least, because it has a third syntax: one
           | for unsafe.
        
             | kaba0 wrote:
             | You quickly gets into the undecidable category once you to
             | down that road, which might be fine if you either don't
             | care about safety, or have runtime checks for that, but
             | none of those were an option for rust (or at least an
             | option that would have made it remotely interesting).
        
               | zozbot234 wrote:
               | It's undecidable in the general case, but it becomes
               | doable in non-Turing-complete languages that are still
               | expressive enough to be useful for many practical cases.
               | (These languages can express unrestricted recursion as an
               | I/O-like effect that's only available outside the
               | language proper, as part of compiling to a binary.)
        
               | kaba0 wrote:
               | Lifetimes only work because they are very restrictive -
               | the moment you can't decide that you end up with a non-
               | safe language.
        
               | mjburgess wrote:
               | Soundness, Completeness, Saftey -- I care about these far
               | less than
               | 
               | Safe, Sound, Complete 99.999% of the time; a joy to use
               | 100% of the time
        
               | kaba0 wrote:
               | Then you are not the target audience of a low-level
               | language that supposed to run without a runtime, but
               | still safely. Don't try to change the tool, when you
               | could just choose a more fitting one.
        
               | zozbot234 wrote:
               | Rust has an unsafe subset for that 0.001% of tne time you
               | need it.
        
               | steveklabnik wrote:
               | (Unsafe is a superset, not a subset, incidentally)
        
             | awestroke wrote:
             | > Delete half of rust's symbols, get rid of its macro
             | system, rewrite it until lifetimes are inferred /or/
             | allocators are explicitly chosen, etc. etc.
             | 
             | Feel free to do this. I predict you will end up with a
             | language that appeals to nobody but yourself
        
               | mjburgess wrote:
               | Well, I'm more-or-less describing Nim.
               | 
               | I imagine it has more users than just me.
               | 
               | My version would, of course, be Nim++
        
             | avgcorrection wrote:
             | > The major innovation in this space is to make the
             | compiler an interpreter which can evaluate the language at
             | compile time (and emits code for runtime where it cannot).
             | 
             | Show us an extant language that does this, then.
             | 
             | Partial Evaluation (automatic) was a very unsolved problem,
             | last I looked.
        
             | duped wrote:
             | > This "two syntaxes, one for the runtime and one for the
             | compiler" approach -- has swamped Rust as it aims for
             | greater expressiveness. Not least, because it has a third
             | syntax: one for unsafe.
             | 
             | What are you referencing, concretely? For example, "unsafe"
             | doesn't add or remove syntax.
        
               | mjburgess wrote:
               | Split the syntax into whether it 1) emits no code
               | (compiler-facing); 2) emits weakly-related code (eg.,
               | macros, etc.); 3) emits code close to what's written
               | abstractly (eg., value operations); 4) emits code close
               | to what the machine needs (eg., unsafe code) -- etc.
               | 
               | And you'll find about "4 languages" all mixed together.
        
               | duped wrote:
               | I still have no idea what you're talking about. Can you
               | give an example?
               | 
               | Most "syntax" in AOT, statically typed languages does not
               | directly generate machine code, but it does directly
               | impact what machine code is generated. So there's not a
               | clear distinction in practice.
               | 
               | For example, a lot of the syntax is used to control
               | method selection and verification - that's not a
               | "different" syntax or language by most folks'
               | definitions.
        
               | mjburgess wrote:
               | Compare this with lisp, where there's _one_ syntax --
               | compiler operations are  "compile-time" interpretations
               | of the very same syntax.
               | 
               | Or below, let's invent a language where there's (in my
               | sense) "one syntax for everything",
               | 
               | Eg., consider something like,                   const
               | SimpleTrait = trait with:            val name
               | def GenericApiTrait(type T) =             return new
               | trait with:                  def response : self -> str
               | const MyAPI = GenericApiTrait(SimpleTrait(new class))
               | with:             val name = "World"             def
               | response : self -> str = f"Hello $name!"              #
               | later on, in the app,              def calc(x, y) : int,
               | int -> int =              return x + y              #
               | ie., the **same** syntax as that earlier which targets
               | compile-time
               | 
               | Here you can see that code with ordinary run-time
               | semantics is used for the compile-time operations of
               | generating a trait and specifying that a class implements
               | that trait (ie., we call the trait-defining function on
               | the class).
               | 
               | Whereas in Rust, the syntax for returning values and
               | "quantifying over types" is radically different.
               | 
               | This is the historical approach (C++, C# .. almost all
               | langs) -- but not one modern innovative langs follow.
               | 
               | I think with as much desire for novelty rust has,
               | cramming it all into its "compile-time syntax" has
               | hobbled it.
               | 
               | A simple uniform language for both c-time and r-time
               | could be used
        
               | duped wrote:
               | Ah ok, I see what you're saying. It's an interesting
               | idea.
               | 
               | One of the reasons that something like unified syntax is
               | not preferred is because there are some important
               | distinctions between runtime and comptime, and a lisp-
               | like syntax doesn't translate well because lisp doesn't
               | have a meaningful distinction between the two.
               | 
               | For example, it's desirable for some folks to have the
               | "comptime" work (declaring types, imports, function
               | signatures, etc) to have a purely declarative syntax. And
               | languages like Rust use declarative syntax to express
               | things about types, interfaces, and their constraints.
               | Contrast that with the logic of the program which is more
               | convenient for a programmer to express in the imperative
               | style.
        
       | pmontra wrote:
       | > The priorities I had while working on the language are broadly
       | not the revealed priorities of the community that's developed
       | around the language in the years since
       | 
       | Isn't that a matter of self selection? A language which developed
       | around those priorities would have a community sharing those
       | priorities now.
       | 
       | The point is if that community would be as large as the current
       | one, larger, smaller.
        
         | kryptiskt wrote:
         | It's not really mentioned in the post, but one thing Rust had
         | since fairly early on was a use case, it was used to build a
         | browser engine. As I remember it from the time, the feedback
         | from Servo development tended to pull Rust in a more
         | performance-oriented direction.
        
         | layer8 wrote:
         | The fact that the community that formed trended in that way can
         | be taken as an indication that those directions attracted more
         | people than the original vision. Note how often Graydon was
         | basically outvoted on design decisions. It would have been
         | conceivable for the project to attract mostly people that fully
         | shared Graydon's vision, but that's apparently not what
         | happened.
        
       | w10-1 wrote:
       | Graydon may lament the features that got away, but being a good
       | loser may be the best way to encourage contributions and to
       | respond to community needs. The historical forces on a language
       | are both its constraints and its drivers. Languages that are nice
       | conceptually are not responsive to history.
       | 
       | Both Swift and Rust both veered away from their original
       | champions. The champions helped by focusing the problem and
       | providing a technical skeleton, but the need (the pain of
       | C/C++/Objective-C) was both intense and complex, so the community
       | was stronger than the BDFL model.
       | 
       | Interestingly, Swift has seen Rust forge ahead on a number of
       | fronts, but is quietly adopting the best of Rust, and soon
       | interoperating with C/C++ will be frictionless. The ties to Apple
       | are being loosened, with a more portable stdlib and a Foundation
       | library that subsets the legacy Apple Foundation instead of
       | dragging Apple API's into other platforms. If/since Apple is to
       | rewrite its systems in Swift, Swift will likely evolve into the
       | best language for migrating off C/C++.
       | 
       | Compare Graydon, von Rossum, or Chris Lattner to Java's Mark
       | Reinhold. Mark has been quietly at the helm of Java since 1997,
       | navigating: the Oracle and open-source transitions, partners
       | ranging from IBM to broad developer communities, continuous VM
       | updates that kept Java relevant, and the quick pace of recent
       | language/library upgrades: lambdas (method and field handles),
       | vector processing and FFI, native...
        
       | bazoom42 wrote:
       | To be successful, it is not enough for a language to be good. It
       | might not even be necessary. What matters is if there is s
       | significant niche where the language is a better fit than any
       | alternative.
       | 
       | PHP show that a language only needs to get that one thing right.
       | 
       | Rust have found its niche. Graydons vision seem to be a more
       | elegant language which would compromize on the points which
       | actully make Rust succesful.
        
         | kaba0 wrote:
         | Well put. Rust wouldn't be anywhere near this big/hyped if it
         | would have been a new ML.
        
         | sapiogram wrote:
         | > What matters is if there is s significant niche where the
         | language is a better fit than any alternative.
         | 
         | This is a wonderful comment, and puts into words something I've
         | been thinking for a long time. The best general-purpose
         | languages always seem to start out with a strong niche, then
         | grow from there.
        
       | 0xDEF wrote:
       | Rust moved in a direction where it is now a suitable alternative
       | to C/C++ all the way down to operating system code and bare-metal
       | embedded firmware.
       | 
       | Graydon wanted something else even before Rust 1.0 was released.
       | He wanted an OCaml-like language with modern Go/Erlang-inspired
       | higher level concurrency abstractions.
        
       | zozbot234 wrote:
       | Technically, Rust had no future prior to the 2018 edition. The
       | fact that Rust can add new features as the use cases for it
       | evolve is a strength of the language, one that it had from the
       | start even with Graydon as a BDFL.
        
       | sph wrote:
       | Very interesting insight from Graydon, in hindsight I too would
       | have loved something more towards ML than C++. I never liked the
       | kitchen sink approach that I see first C++, now Rust moving
       | towards, but I respect what Rust has managed to solidify into.
       | It's a good language.
       | 
       | That said, I still hate async with a passion, it makes the
       | language more complex and not very elegant (i.e. function
       | coloring). And now that I know how it works behind the scene
       | (thanks to Jon Gjengset [1]), it feels so complicated and hacky,
       | a mediocre very high level concept that someone managed to
       | implement as a zero-cost abstraction. Impressive, but still a bad
       | idea.
       | 
       | I'm sure the pro of having a BDFL instead of a committee is being
       | able to follow a singular vision, instead of trying to appease
       | members by adding the fad du jour which might stray a little too
       | far from the original vision. Too many chefs in the kitchen and
       | all.
       | 
       | 1: https://www.youtube.com/watch?v=ThjvMReOXYM
        
         | throwawaymaths wrote:
         | > a zero-cost abstraction
         | 
         | It's just an abstraction, it's not zero-(runtime)-cost. It
         | _might_ be the  "lowest possible cost", still nonzero.
        
         | littlestymaar wrote:
         | > (i.e. function coloring)
         | 
         | I really wish people stopped using this concept, especially in
         | the context of Rust because the `async fn`/"regular function"
         | split is _strictly equivalent_ to the `try_something()`
         | /`something()` split (the first one being failible and
         | returning a `Result` in case of failure). `Result`s and
         | `Option`s are coloring the stack in _exactly the same way_ a
         | `Future` does (and `async` is pure syntactic sugar on top of
         | future).
         | 
         | So, someone may like exceptions and green threads more than
         | `Result` and `async` (and this is a completely valid PoV, even
         | though I personnaly like the explicitness better), but thinking
         | `async` is somehow special is just a conceptual mistake.
         | 
         | BTW, in the original blog post the "red" functions were
         | actually functions _with a callback parameter_ , which is
         | actually very different.
        
           | chmod600 wrote:
           | There's consistent use of Result and Option in std, community
           | agreement to do things that way, and it's pretty easy to use
           | them.
           | 
           | Tons of stuff in std and many other crates don't use async. A
           | lot of stuff is harder and more code to do async. A lot of it
           | is much harder to read.
           | 
           | That's the difference.
        
             | littlestymaar wrote:
             | > There's consistent use of Result and Option in std,
             | 
             | Well, there are dozens[0] of incompatible error types in
             | std, two of them being just called `Error`[1][2].
             | 
             | Everytime you create a function that use more one of the
             | different error types from std (for instance, an I/O error
             | and a _TryFrom_ -related error) you have to create a custom
             | Error enum to accommodate the different kinds of errors
             | from std...
             | 
             | > community agreement to do things that way
             | 
             | And the community agreement is error-chain, failure,
             | snafu, this_error, unless it's for a binary crate and then
             | it's anyhow, eyre, color-eyre.
             | 
             | The error ecosystem is much more fragmented than the async
             | one (even though I think _this_error_ is here to last, so I
             | don 't have to migrate once again...). In the async world,
             | there's pretty much _tokio_ (but I think it would actually
             | be better if it interoperated better with other runtimes so
             | alternatives could emerge)
             | 
             | > A lot of it is much harder to read.
             | 
             | Ah yes, _fut.await_ is fundamentally harder to read than
             | _res?_ , right?
             | 
             | Overall, you vastly underestimate the friction that the
             | `Result`-based error handling adds. I still think it's
             | worth it, and it's been slowly getting better (did I say I
             | loved _this_error_?), but it remained one of the biggest
             | point of friction in Rust over the 8 years (time flies) I
             | 've been using it. Async is comparatively less disrupting
             | (mostly because it only appears in the call stacks where
             | you're doing I/O, whereas errors are ubiquitous).
             | 
             | [0]: https://doc.rust-
             | lang.org/std/error/trait.Error.html?search=...
             | 
             | [1]: https://doc.rust-lang.org/std/io/struct.Error.html
             | 
             | [2]: https://doc.rust-lang.org/std/fmt/struct.Error.html
        
         | pornel wrote:
         | Function coloring article conflated two things: one was
         | legitimate limitation of JS unable to wait for async result
         | from sync call, and the other was just author's opinion how the
         | syntax should look like.
         | 
         | Rust's async doesn't have the limitation author described - you
         | can spawn and block both (Tokio has some limits there, but
         | that's Tokyo's choice, not language limitation), making "color"
         | largely irrelevant.
         | 
         | The second point was that both should have identical syntax,
         | which Rust deliberately chose not to, because from Rusts
         | perspective that would be too much implicit magic.
        
           | sph wrote:
           | I disagree. You can call an async function in Rust from a
           | regular one, that's true, but the returned value still needs
           | to be passed through an executor to be useful.
           | 
           | Async without function coloring means being able to call
           | `async fn add(a, b: usize) -> usize` from anywhere and having
           | an usize back, whether you're calling from a regular or async
           | function. If you need slightly different logic because of
           | async, you got function coloring.
           | 
           | In fact, a hypothetical _async without coloring_
           | functionality would not even need the `async` keyword at all,
           | as all functions would be effectively the same, so you could
           | choose to call one asynchronously or not with no particular
           | ceremony. Is this even possible to do without compiler magic?
        
             | xmcqdpt2 wrote:
             | You can do it using a runtime with scheduling and fibers.
             | 
             | The issue is that you want to be able to save the entire
             | thread stack cheaply so that you can switch to another task
             | at an async yield point, and then go back to your previous
             | stack frame. You want to do this without spawning system
             | threads.
             | 
             | Say you have an f -> g -> h call stack that blocks on IO.
             | If all the functions are in fact async state machines (or
             | some other kind of callbacks) your thread stack will look
             | like this:
             | 
             | Executor.loop() -> Executor.run(h) -> h.await()
             | 
             | That is, your functions are really objects that are queued
             | up and taken as needed by the executor. If h has a yield
             | point, you can put it back on the queue, go back to the
             | loop and pick up some other work. Later you can do the
             | above again and await h to again up to the next yield
             | point.
             | 
             | Now consider the case where the middle function g is NOT
             | async. In that case the call from f to g will be a normal
             | function call that gets put on your call stack. In a
             | language where this is allowed, you'll still have to wait
             | or execute h and you'll get a call stack that looks like,
             | 
             | Executor.loop() -> Executor.run(f) -> f.await() -> g() ->
             | h.awaitUntilCompletion()
             | 
             | (where our awaitUntilCompletion is minimally just calling
             | h.await in a loop until h is finished). At this point, you
             | are stuck: you can't yield from g() because it is a normal
             | function with a normal stack frame, so your thread has to
             | wait till g() finishes before it can be used for anything
             | else. You are basically blocking a thread on h. At this
             | point you might spawn a new thread to keep your thread pool
             | count up (which is expensive and memory hungry) or just
             | accept the forced synchronous blocking (which reduces
             | throughput).
             | 
             | If this happens over and over again, you either end up
             | running out of memory or end up running code in a
             | synchronous fashion but with worse performance (because of
             | all the state machines). This is why functions are usually
             | coloured, so that you don't get yourself in this accursed
             | state by accident. AFAIK the alternative is to allocate all
             | of your call stacks in the heap so that you can switch them
             | in and out of kernel threads, which is what fibers are.
             | This requires a complicated runtime with a scheduler such
             | as in Loom,
             | 
             | https://cr.openjdk.org/~rpressler/loom/Loom-Proposal.html
        
             | ohgodplsno wrote:
             | Not knowing about function coloring is a stupid dream and
             | has terrible implications on the performance of your code,
             | which is infinitely more important than you losing 2
             | minutes having to figure out that you really want to
             | `runBlocking { callThatBlocksForADamnLongTime() }`.
             | 
             | The insight that function coloring gives you is terribly
             | important. Roman Elizarov (of Jetbrains, author of Kotlin
             | and lead on Kotlin Coroutines) agrees too:
             | https://elizarov.medium.com/how-do-you-color-your-
             | functions-...
             | 
             | async functions without coloring means that the only
             | warning you'll ever get that `calculate2Plus2()` actually
             | ends up running a distributed BigQuery and writing the end
             | result to disk, printing it to stdout() and parsing that
             | result to give it back to you is... hopefully,
             | documentation is up to date and you read it?
             | 
             | Async function coloring is not a problem. Async function
             | coloring is a solution to "software developers are awful
             | and will do awful things without any warning". If
             | `calculate2Plus2` did not exhibit the write to disk
             | behaviour in v1.0.0, but bumping to v1.0.1 does, I'd really
             | want a warning that it does at least, and ideally a
             | compiler error. The proper solution to function coloring is
             | to have a pleasant API to interop between both worlds so
             | that, at worst it's just a dozen characters more to say
             | "yep, I really want to block here".
        
               | mike_hearn wrote:
               | Async doesn't solve that problem. What you're asking for
               | is some sort of expression in the type system of expected
               | performance, but there's no tech that can do this.
               | Consider that modern NVMe SSDs can do disk reads faster
               | than many calculations over the content of what was just
               | read. A function that changes its algorithm to have worse
               | time complexity won't be marked as async but could break
               | your app, adding a single disk read would require marking
               | as async but probably won't break your app (especially if
               | it's in a non-hot path), adding 10,000 more reads where
               | previously you had one doesn't require changing your
               | async annotation but might break your app, etc.
               | 
               | So the whole sync/async distinction doesn't really make
               | much sense outside the context of single threaded UI
               | toolkits, where indeed you have to read the docs anyway
               | because doing some very CPU intensive work on the UI
               | thread will block it even if no "blocking" APIs are ever
               | invoked (and what is or is not blocking is somewhat
               | arbitrary anyway in many cases).
        
               | hayley-patton wrote:
               | There are type systems which can track complexity (e.g. h
               | ttp://gallium.inria.fr/~fpottier/slides/fpottier-2011-01-
               | jf...) but that's still different to performance;
               | async/sync tells us things like "touches disk" or
               | "touches network" but not "O(2^n) might take a really
               | long time".
               | 
               | Alternately, an effect system can communicate "touches
               | disk", can make functions which are polymorphic to
               | effects (e.g. map touches the disk if the mapping
               | function touches the disk), and can distinguish different
               | effects, unlike async/sync being anything-or-nothing. IMO
               | async is more of an implementation detail (as "may
               | suspend/yield" is not a very precise or interesting
               | effect), whereas effects would communicate the properties
               | that ohgodplsno wants to know.
        
               | mike_hearn wrote:
               | Yes, but are there any effects systems in use outside of
               | maybe Haskell? Effects seem mostly to be stuck in the
               | research lab and have been for a long time.
        
               | hayley-patton wrote:
               | I don't mean "effects" in the sense where you can have
               | effect handlers which get delimited continuations and
               | all; it'd just mark what could happen (like checked
               | exceptions). But I can't think of any languages with that
               | and not effect handlers; Koka, Eff, and Unison come to
               | mind for effects, though their practical-ness may vary.
        
               | zozbot234 wrote:
               | Do you really need effect handlers as a separate feature
               | once you have async/await? It seems like you could easily
               | encode one with the other.
        
               | hayley-patton wrote:
               | An effect handler is allowed to invoke a continuation
               | multiple times; I believe this is not possible with
               | async/await.
        
               | ohgodplsno wrote:
               | Verse has effects, which by virtue of exposing it to the
               | entire Fortnite community probably makes it a bigger
               | language than Haskell in terms of user counts.
               | 
               | https://dev.epicgames.com/documentation/en-
               | us/uefn/specifier...
               | 
               | converges/diverges, computes, varies, reads, writes,
               | transacts, no_rollback.
        
               | ohgodplsno wrote:
               | Adding a disk read won't break my software, but it can
               | certainly make my software non-portable. If it suddenly
               | expects a disk write and I'm running on a ROM, I'm going
               | to have a hard time. If it suddenly tries to contact
               | google.com and I'm on an airgapped machine, I'm going to
               | have a hard time. It's not even a matter of breaking the
               | app actually, it's a matter of warning that "I'm going to
               | go outside the realm of just your CPU and your memory".
               | Sure, you could also mark as as async a function that
               | adds two numbers. It's stupid, it's a JS thing to do, but
               | you could. Except that you need to actively opt-in to
               | being async in this case, as opposed to have it be forced
               | upon you because you used a function that is also async.
               | 
               | Blocking has meaning in a lot more contexts, and being a
               | consultant on JVM related topics, you should know that:
               | it's the entire purpose of Project Loom, and Loom doesn't
               | entirely get rid of colour coding either explicitly for
               | that purpose. Loom wasn't made because the guys at Oracle
               | have a deep love for JavaFX, but rather takes into
               | account the server world, where you really want to know
               | that you're going into another context, another computer,
               | etc. The only time where the existence of async doesn't
               | make sense is if your entire language and ecosystem
               | expects everything to already be distributed. In which
               | case, you've just switched the default color to async.
               | 
               | Finally, you chose to read async as the current JS/C#
               | abomination implementation, but most of the sensible
               | languages have implemented it as an effect: Kotlin has
               | suspend funs, they don't return a Promise, but they tell
               | you two things: they're going to touch something like the
               | disk or the network, and if you really want to have them
               | in a non-suspend context, you can either get a
               | Deferred<T> out of them (and find another thread to run
               | it on, and handle synchronization yourself), or run them
               | on the current thread (and block everything).
        
               | coldtea wrote:
               | > _Adding a disk read won 't break my software, but it
               | can certainly make my software non-portable. If it
               | suddenly expects a disk write and I'm running on a ROM,
               | I'm going to have a hard time. If it suddenly tries to
               | contact google.com and I'm on an airgapped machine, I'm
               | going to have a hard time. It's not even a matter of
               | breaking the app actually, it's a matter of warning that
               | "I'm going to go outside the realm of just your CPU and
               | your memory". Sure, you could also mark as as async a
               | function that adds two numbers. It's stupid, it's a JS
               | thing to do, but you could._
               | 
               | You're conflating too many orthogonal things.
               | 
               | It's not merely because of the performance of touches
               | disk/network that async was used for those cases, it's
               | because that waiting is not because you're held up by the
               | language doing calculations. That isn't the case with a
               | function like you describe.
               | 
               | Marking functions async when they aren't yielding just to
               | signal that they might be slow is a bizzare idea. That's
               | not what async is and it doesn't bring any real benefit,
               | it's just abusing the notion (and limiting the contexts
               | where you can run those functions). You'll still be using
               | libraries which wont follow this strange idea, and you
               | should know their performance characteristics.
               | 
               | Blocking code exists in all major languages, including
               | JS. In a single thread context, having something "async"
               | wont help you at all, if it calls anything blocking,
               | which can be something as common as JSON.parse
               | 
               | > _If it suddenly expects a disk write and I 'm running
               | on a ROM, I'm going to have a hard time. If it suddenly
               | tries to contact google.com and I'm on an airgapped
               | machine, I'm going to have a hard time._
               | 
               | All of those have nothing to do with async, and what
               | async is created and used for.
               | 
               | What you want is something like Haskell's IO "tainting",
               | a (side) effects system, or something to that (no pun
               | intended) effect.
        
               | mike_hearn wrote:
               | Blocking in Loom is primarily about waiting for network
               | traffic, possibly with extensions to file IO in future -
               | it doesn't suspend if you do file IO today. Loom does get
               | rid of coloring, or rather, doesn't introduce more of it
               | and lets you phase out what exists, so I'm not sure what
               | exactly you mean by that.
               | 
               | Kotlin suspend funs do _not_ tell you they 're about to
               | touch network disk, that's the reason they use "suspend"
               | and not "async". Suspend funs can also use generators
               | (with yield) in which case no I/O is happening but they
               | are nonetheless suspending.
               | 
               | So this is why blocking as a concept isn't a great one,
               | IMO. The Kotlin designers were right to not use the word
               | in their implementation of coroutines. Where Loom
               | discusses blocking a bit, it's not defined as being
               | _about_ blocking, it 's about being able to scale up
               | threads to way beyond what was previously possible. It
               | just so happens that the primary reason you'd want to do
               | that is if you have lots of threads that spend lots of
               | time waiting for things, but that doesn't automatically
               | require network or disk access. For example you can use
               | threads that spend all their time in Thread.sleep if you
               | were writing an agent simulation.
        
               | kaba0 wrote:
               | > possibly with extensions to file IO in future - it
               | doesn't suspend if you do file IO today
               | 
               | Wasn't basically all of the JDK's file APIs rewritten to
               | io_uring-like calls to support Loom? I have thought that
               | IO was definitely something that Loom handled.
        
               | mike_hearn wrote:
               | Last I heard it was on the todo list, but maybe it got
               | done since I last looked. You can do async IO without
               | io_uring.
        
               | anonymoushn wrote:
               | This comment reads like it's from an alternate dimension
               | where "calling read(2) marks functions as async" is a
               | common language feature. Over here, read(2) can block
               | forever and normal functions can call it in just about
               | every language.
        
             | kaba0 wrote:
             | Virtual threads (like java's loom, erlang's and goroutines)
             | are arguably that.
             | 
             | In case of java there really is no compiler magic even,
             | "just" runtime magic.
        
               | pas wrote:
               | how big is the overhead for this? and how much easier is
               | working with virtual threads than Rust's async?
        
               | winrid wrote:
               | 1. Low enough that you don't need to worry for most web
               | backend code and 2. Anything is easier than that. *XYZ is
               | not Send* amiright?
               | 
               | With Rust's async you have to worry about a different
               | worker thread picking up the work after a context switch,
               | which makes things complicated. Not so with Java.
        
             | josephg wrote:
             | Its certainly possible _with_ compiler magic. Zig does
             | exactly that - where functions can be colorless and they
             | work slightly differently in async and syncronous contexts,
             | doing the obvious thing in both cases. I 'm not sure how
             | well it works in practice (I haven't spent enough time with
             | zig to know). I'd worry that bugs would creep in to
             | whichever variant of the function you aren't exercising.
             | 
             | Its a lovely design though.
             | 
             | https://kristoff.it/blog/zig-colorblind-async-await/
        
               | svieira wrote:
               | _Unyielding_ describes the issues that arise when the
               | author of the code doesn't have control of the
               | transactionality / atomicity of their operations at the
               | level they need to:
               | https://glyph.twistedmatrix.com/2014/02/unyielding.html
               | 
               | To my mind it's very much a balancing act between "low
               | power to the developer, high power to the language" and
               | "high power to the developer, low power to the language"
               | all up and down the stack from software / hardware to
               | "consumer / framework".
        
               | svieira wrote:
               | See also:
               | https://www.tedinski.com/2018/11/06/concurrency-
               | models.html which is the first article I've ever seen
               | that says that `m:n`-style-green-threads are bad and good
               | at the same time.
               | 
               | TL;DR - he says that "doParallel" and "doConcurrently"
               | are separate operations with distinct semantics that
               | designers of programs must care about and that conflating
               | the two (especially the common "doConcurrently-and-often-
               | but-not-always-in-parallel") is one of the most common
               | causes of bugs in programs that need to make progress in
               | multiple threads of execution.
        
             | creatonez wrote:
             | Thing is, it's a function coloring that mostly doesn't
             | impede you by making certain operations impossible without
             | massive refactoring. Regardless of an extra method call
             | needed, it gets rid of the "you can only call a red
             | function from within another red function" barrier by
             | allowing you to do it, as long as you specify how (execute
             | inside this thread, or delegate to a worker thread). This
             | makes most of the issues associated with Javascript
             | function coloring evaporate.
        
               | sph wrote:
               | Fair enough. It's not a deal-breaker kind of coloring, my
               | issue is mostly conceptual. It's simply not elegant as a
               | construct, but perhaps it doesn't have to be.
        
             | belmont_sup wrote:
             | Algebraic effects like how OCaml's now implementing it may
             | be possible! The ecosystem around effects in OCaml is still
             | really young, but here's an example of an http request
             | being made that is asynchronous, non-blocking but looks
             | synchronous with no special syntax.
             | 
             | https://github.com/mirage/ocaml-
             | cohttp/blob/16e991ec1f7e5f0c...
             | 
             | Performing these effects is similar to throwing exceptions
             | up the callstack where whichever ancestor handles the IO
             | work, then resumes the child with IO work done in hand.
        
               | mjmahone17 wrote:
               | As someone who likes OCaml, but hasn't touched OCaml in
               | ~5 years, that's a very hard example to read. I can tell
               | it's making a network request, but I have no clue:
               | 
               | - what the type of res is (is it a string? A buffer?
               | Async string? Something else?)
               | 
               | - how this code does not block: what is happening in
               | parallel? At which point will it block?
               | 
               | I would like to be excited about OCaml's algebraic
               | effects, but right now I don't really get it.
        
               | yawaramin wrote:
               | - It's some type from which you can read a response body.
               | In OCaml we often work in terms of abstract types i.e.
               | the operations which can be done on types. If you want to
               | see the exact name of the type you can always look up the
               | signature of `Client.get`:
               | https://github.com/mirage/ocaml-
               | cohttp/blob/16e991ec1f7e5f0c... (nowadays LSP support in
               | any good editor will show the type on hover)
               | 
               | - It's implementing non-blocking I/O using effect
               | handlers. The complexity is not exposed to the library
               | user, which is actually the whole point. If you want to
               | dive into the concurrency library (Eio) and study how it
               | works, that's very doable, just like any concurrency
               | library in any other language.
               | 
               | There's really not that much to get-OCaml will do async
               | I/O without having to colour functions, just like Go,
               | Erlang/Elixir, and soon Java. It's not something sexy
               | like monads that excite people with mysterious FP
               | concepts, it's just a lot of hard work that went into
               | simplifying how developers code at scale.
        
           | moth-fuzz wrote:
           | You can't evaluate a future in normal rust as there's no
           | default executor, you need to pull in some library to even
           | make _blocking_ calls to async functions.
           | 
           | IMO, this is even worse than function coloring.
        
             | jarjoura wrote:
             | It's the same in C++. The compiler and language design came
             | first and then the library (aka executor) part comes next.
             | 
             | Rust has basically 2 executor libraries, tokio and async-
             | std. It seems to me like tokio is solidifying as the
             | executor of choice and it's only a matter of time before
             | that design is baked into the std library.
        
             | weinzierl wrote:
             | It's a bit strange indeed. On the other hand you can't
             | allocate either if you don't have an allocator. The
             | difference is just that the allocator is opt-out while the
             | reactor and executor are opt-in with no formal default.
        
             | zanellato19 wrote:
             | You can also make your own executor. That's exactly how it
             | was intended.
        
           | moron4hire wrote:
           | I've been using C# and (Java|Type)Script pretty much since
           | they were invented. Both use a function coloring async
           | system. I don't know what it's like in Rust, but at least in
           | the two examples with which I have experience with this
           | apparently much-maligned system, I really don't get the
           | complaints. Having to "color" functions really isn't that big
           | of a deal.
        
           | z3t4 wrote:
           | JS has first class functions that you can pass around. The
           | different colors debate was just a confusion - there where
           | only one type of function, just that some functions took a
           | function as an argument, althgough JS has no async functions.
           | All async functions in JS comes from the runtime, like
           | addEventListener for web DOM events. The "solution" to the
           | non existing problem the committie came up with was to
           | introduce 3 new types of function. So JS went from one type
           | of function, to four types of functions.
        
         | divan wrote:
         | It's crazy that the programming community even accepted the
         | concept of async/await as a sane one.
         | 
         | Being sync or async is essentially a property of the attention
         | of the caller, not of the action itself. Is "eating a donut" a
         | sync or async action? If I'm focusing all my attention on it,
         | essentially putting all tasks aside (after) - then it's a
         | synchronous action. If I'm reading a book/watching a
         | video/walking/etc, while eating - it's an async action.
         | 
         | How does "func EatADonut() async {}" aka "eating a donut is an
         | inherently async action" even make sense to people?
        
           | pmoleri wrote:
           | > How does "func EatADonut() async {}" aka "eating a donut is
           | an inherently async action" even make sense to people?
           | 
           | Of course it does, read it as "beware, something blocking
           | down the road".
           | 
           | If you can EatADonout without blocking, please do, but want
           | it or not that's a different implementation, one that doesn't
           | block and the signature it's telling you so.
           | 
           | We're so used to sync and having hidden blocking operations.
           | I wonder if in an alternate universe the first languages
           | considered the blocking/async nature of operations and then
           | some newer languages considered hiding this information into
           | seemingly sync functions would produce similar but opposite
           | outrage against it.
        
             | [deleted]
        
             | divan wrote:
             | > want it or not that's a different implementation,
             | 
             | Implementation is the same. In both cases it's the same set
             | of CPU instructions, but async/await languages create
             | artifical division, forcing developer to think othervise.
             | 
             | So, let me explian my reasoning. Code starts with a
             | developer's mental model of a problem and behavior of the
             | system and then translating it into the code. The more
             | straightforward this translation, the more readable the
             | code. Code is a second degree map of problem domain so to
             | speak ("real" world -> mental model -> code).
             | 
             | Like if you want to add two values, the simplest form of
             | code would be "add(2,2)" which is pretty straightforward.
             | If the code forces you to do some mental gymnastics (i.e.
             | "2 2 addOnlyEvenNumbers") - that's less straightforward,
             | less clear and less readable.
             | 
             | In the same vein, if you want to execute some function
             | ("EatADonut" or "MakeHTTPCall") - you may care or not care
             | about blocking and waiting for results. But it's your call.
             | So it makes sense to give you two options to run this
             | function. Go has simplest possible solution - "eatADonut()"
             | vs "go eatADonut()". It doesn't matter what is a "default"
             | here - it could be "eatADonut()" (go to background) vs
             | "sync eatADount()". What matters that "eating a donut" is
             | just a set of instructions to the CPU, and it's up to
             | caller to decide how you want to execute it in terms of
             | concurrency.
             | 
             | Now, "async/await" approach turned this ownership of
             | "synchronicity" around. Now function is deciding how it
             | should be called. Mental model of "actions" now needs to be
             | translated into "actions being async or sync for the
             | purpose of fitting into this language concept". Which is
             | cognitively expensive for no added benefit.
             | 
             | Sure you can rationalize it, and get used to it as to any
             | other absurd design, but it still adds unneeded complexity
             | to the code, makes it less readable and less clear.
        
           | paulddraper wrote:
           | IDK about donuts.
           | 
           | But consider a network request. The vast majority of the time
           | is not spent in the CPU.
           | 
           | Right?
        
             | divan wrote:
             | Doesn't matter, donut was just an abstraction.
             | 
             | s/eat a donut/make a network request/
             | 
             | Is "network request" a synchronous or asynchronous
             | activity? It depends whether your code blocks and wait's
             | until response (or timeout) or continues executing and
             | handling it when it comes. It's property of the "attention"
             | of the caller, not of activity itself.
        
               | joshuamorton wrote:
               | But "can be gracefully executed asynchronously" is a
               | property of an action.
               | 
               | Something that pegs the CPU to 100% because it's doing
               | intense processing isn't a good candidate for async.
               | Similarly, some code can cause issues when written in a
               | non-streaming fashion. Take the following example (in
               | python):                   x = [x for x in
               | range(1_000_000_000)]         y = (x for x in
               | range(1_000_000_000))
               | 
               | If you spawn a bunch of async workers doing the first one
               | concurrently, you'll OOM your system. If you spawn a
               | bunch of async workers doing the second one concurrently,
               | your system will be fine.
               | 
               | In other words, "async" is a label on a box of donuts
               | that implies (though doesn't ensure, people can of course
               | still do bad things) that the donut won't explode if you
               | look away from it.
        
               | divan wrote:
               | Hmm, is it a post-factum rationalization or it's the
               | original logic behind async/await? Let's mark "heavy"
               | functions with a label, so user has to call them
               | differently not to overload the system?
               | 
               | Even if this is an original logic, why language is
               | deciding for me what is considered heavy or not? What if
               | I'm fully aware that I'm doing heavy processing and I
               | want it to be happening in the background. What if I'm
               | writing HFT software and every call is heavy for me.
               | Language is not a right level of abstraction of marking
               | "heaviness" of the code.
               | 
               | It really just doesn't make sense. Why stop there and not
               | start marking functions with how many times per second
               | they can be called? Like you can call "light" function
               | 100 trillion times per second and OOM the system, so
               | let's mark it "func light() async 2_times_per_second {}".
               | It only can be called from functions that have lower per
               | second label.
               | 
               | Plus, if Python cares so much about not OOM-ing the
               | system I would start by not requiring 32-bit int to
               | occupy 28 bytes in the first place.
        
               | wizofaus wrote:
               | It's only functions that "block and wait" that it makes
               | sense to be async - the point being that blocking and
               | waiting doesn't use up CPU cycles, so you might as well
               | free the thread up to do other stuff. If a function
               | issues a socket write, then continues utilising the CPU
               | while also periodically checking if data's available to
               | read, it's effectively doing manually what async does for
               | you, though I'm not sure there are too many real life
               | examples of such functions. But pre-async it was
               | certainly common enough for block-and-wait functions to
               | tie up a thread and hence execution of programs with
               | limited multi-threading (even today, GUIs often require
               | all events and updates to be processed on the primary
               | thread).
        
           | bourneavent wrote:
           | >How does "func EatADonut() async {}" aka "eating a donut is
           | an inherently async action" even make sense to people?
           | 
           | You have to think of it from a higher level.
           | 
           | Nobody knows if eatdonut is strictly async but everyone knows
           | all IO calls are async. So something like socket.send would
           | be async, this is obvious.
           | 
           | Then anything that calls socket.send would then in turn
           | become async itself. The async sort of spreads around like a
           | virus. Any function that uses other async functions gets
           | polluted with the async marker.
           | 
           | So in this sense, you should think of it like this. If
           | eatADonut is async it means it touches IO. It sends data
           | somewhere else to some other thing. It also means it's not
           | unit testable.
           | 
           | In a nut shell This is the intuition you should have about
           | async:                  Async Tells You about the properties
           | of eatADonut. eatADonut doesn't tell you anything about
           | whether or not it's async.
           | 
           | This is largely identical to how the IO monad works in
           | Haskell. Unless you want all your logic polluted with the IO
           | monadic wrapper or the async marker you have to learn to
           | segregate your functions into async functions and non-async
           | functions.
           | 
           | Under this intuition, EatADonut, by good design principles
           | should NOT be async. This is what it should be:
           | fn eatAdonut() -> ResultOfEatADonut         async fn
           | sendDataToIO(data: Data) -> ()              async
           | parentCaller() -> () {            let data = eatAdonut();
           | sendDataToIO(data.serialize()).await;            ()         }
           | 
           | The async marker forces you to organize your code in this
           | way, You have to do this unless you want all your functions
           | to be polluted by the async marker. This is the proper way to
           | organize code Anyway, as it allows your code to be unit
           | testable. Async calls are, in general, not unit testable
           | because the functions are not pure. So if you look at what I
           | did above, I segregated eatAdonut Away from IO and made part
           | of the logic of the overall code testable via unit tests.
           | 
           | IO calls should be very general and small functions while
           | your other functions should be pure and unit testable. This
           | is the overall intuition you should have about async.
           | 
           | Believe it or not, this specific aspect of async is actually
           | an improvement over golang style concurrency. Not an overall
           | improvement, it's still debatable which style is better, but
           | I'm saying the async marker is actually a feature that other
           | styles of implementing concurrency don't offer.
           | 
           | Basically, async encodes the concept of IO and function
           | purity into the type system. It allows the type checker and
           | You to tell which functions touch IO and which functions are
           | pure and unit testable.
           | 
           | People think unit testability is some kind of marginal
           | benefit because you can cover testing with integration tests
           | which are basically tests that happen across IO. It's more
           | than that. The ultimate dream of programming something as if
           | it's a bunch of modular lego blocks that you can elegantly
           | place together is most fully realized with pure functions.
           | Async enables more of this style of modularity in Rust (but
           | not without some cost which is why it's debatable whether or
           | not it's better than go style concurrency).
        
           | kmac_ wrote:
           | How about waiting until all or part of donut pack is eaten by
           | multiple eaters? Jokes aside, async/await comes from easier
           | handling of a callback code and automatic function splitting.
           | Writing asynchronous donut eating in loops or with yielding
           | of partial results is very easy to swallow with such
           | syntactic sugar. The whole model is very easy to grasp and to
           | work with. I'm not saying that's the best solution but it
           | definitely works well.
           | 
           | Regarding stickiness of function colors - it never happens
           | when async/await is used correctly (in the same principle as
           | IO monad isn't sticky).
        
             | divan wrote:
             | Doesn't matter. You either wait until donut is eaten by
             | others or you don't. Caller is deciding if it's
             | "synchronous" (blocking) action or not.
             | 
             | The question is how you translate mental models from real
             | world to the code, and async/await fails here
             | spectacularly. It's just weirdly unnatural thinking about
             | sequencial processes. It requires tremendous amount of
             | cognitive gymnastics just to reason about simple things.
        
           | bmacho wrote:
           | This was my first (and obviously wrong) mental model of how
           | async works.
           | 
           | There are functions, you can call them sync or async if they
           | handle IO or UI or they will get a necessary data later.
           | 
           | I still don't understand why I can't fetch a URL from top
           | level javascript. Also I don't understand why zig async and
           | await passes the control flow that seemingly total arbitrary
           | way. The naive approach (put async calls in a queue, and
           | periodically check if they are completed or can be executed)
           | seems fast, deterministic, and good enough in every way?
           | 
           | Okay, maybe zig needs the speed, and can't just stop the
           | execution of the sync code time to time to do something else,
           | but why javascript? Maybe javascript async is build upon
           | regular promises and regular objects, instead as a proper
           | language element with proper support in the javascript
           | engines? I don't know. Anyway async as is used with colors is
           | totally against the picture that the words "async" and
           | "await" suggest.
        
         | jaxrtech wrote:
         | If Rust was a higher-level language, then I'd say yes, just
         | automatically handle running Futures in parallel, join them
         | when you actually need to resolve the data, and pretend that
         | they look like synchronous functions in the 80% of cases.
         | Though things like `select!` wouldn't make any sense mixing the
         | two.
         | 
         | I continue to find the "function coloring" argument misses the
         | point unless you're arguing from a developer experience
         | perspective. Why should two fundamentally different things look
         | and function the same? Want this the ultimate pitfall of early
         | RPC implementations where everything looks synchronous?
         | 
         | In Rust, a lot of the friction is due to how async
         | functions/futures fundamentally differ in model of execution
         | and how that interplays with the rest of the language. Other
         | languages get to hand-wave a lot of these problems away with a
         | GC. (It certainly could be less of a pain nonetheless.)
         | 
         | - Futures don't execute until polled, can partially execute if
         | something isn't ready (and would block), can partially execute
         | and be arbitrary cancelled and dropped. There is no equivalent
         | in sync functions unless you make them co-routines.
         | 
         | - Since you can "pause" futures if something would block, you
         | need a place to store the function's state for when it's
         | resumed. Now you must be consider if the async function's state
         | is `Send` if the future wants to move to a different thread to
         | continue executing -- which is why you see `pin!` used. Sync
         | functions don't care about this since you always run to
         | completion on the same thread, and the stack is ephemeral.
         | 
         | - Likewise, the `Future` returned by the async function is
         | going to need to encapsulate it's state that it saves. In the
         | general case, this is a compiler generated anonymous struct
         | that changes if any state saved across `.await` changes, hence
         | the opaque `impl Future`. This is why you see `BoxedFuture` a
         | lot to abstract this away at the expense of an allocation.
         | Ideally, the new associated types with lifetimes can avoid this
         | with traits.
         | 
         | So if all functions were co-routines (i.e. functions that can
         | be resumed and re-entered) they would all have the same
         | "color". But all you really did was "lift" all sync functions
         | to be "async" functions with no internal await points.
         | 
         | (IMHO, if the C# team back in the day decided to implement full
         | blown co-routines into the language instead of just
         | `async/await` as a compiler trick, I think many other projects
         | would have followed suit with a more general co-routine
         | solution instead of treating `async/await` as this special
         | thing which is just a specific compiler generated
         | implementation of co-routines.)
        
         | dist1ll wrote:
         | > That said, I still hate async with a passion, it makes the
         | language more complex and not very elegant (i.e. function
         | coloring)
         | 
         | Function coloring seems to come up a lot in these discussions,
         | but I don't see a better way without providing a runtime. Could
         | you propose an alternative approach to async, without
         | sacrificing ability to write zero-overhead, high-performance
         | bare metal systems?
        
           | umanwizard wrote:
           | They could have simply not added async to the language. I was
           | happily using rust before async and it was perfectly fine.
        
             | hgomersall wrote:
             | So don't use it. Why object to those that do have a use
             | case for it?
        
               | umanwizard wrote:
               | Because I have to work with other people (both coworkers
               | and developers of library crates).
        
               | ninkendo wrote:
               | Because it's possible the crate you want to use, uses it.
        
               | hgomersall wrote:
               | Or alternatively the crate you want to use doesn't exist.
        
               | ay wrote:
               | fragmentation of the library ecosystem.
        
         | bsder wrote:
         | > That said, I still hate async with a passion, it makes the
         | language more complex and not very elegant (i.e. function
         | coloring).
         | 
         | async in Rust is a popularity issue even more than a technical
         | issue.
         | 
         | A whopping amount of people who are coming to Rust are doing so
         | because they want a good _ecosystem_ for implementing network
         | service servers. Rust /cargo/crates hits the sweet spot for
         | them.
         | 
         | I am with you that I loathe async/await in Rust. However, I
         | also have to acknowledge that without async/await, Rust is a
         | _vastly_ less popular language.
         | 
         | All the other uses of systems programming are simply _dwarfed_
         | by number of people building network services. That 's just the
         | sad reality.
        
         | fzeindl wrote:
         | I hate async as well. Developers should have learned about
         | communicating sequential processes and blocking queues and none
         | of that would have been necessary. It creates a weird divide in
         | every language. Just learn about threading and do it.
         | 
         | A nice talk about this:
         | https://www.reddit.com/r/programming/comments/da141r/ron_pre...
        
           | deltaninenine wrote:
           | [dead]
        
           | unscaled wrote:
           | I'm pretty sure the designers of Rust knows about CSP. Rust
           | has Channels in the standard library after all:
           | https://doc.rust-lang.org/rust-by-
           | example/std_misc/channels....
           | 
           | Rust Channel have the nice additional that they don't panic,
           | and don't have all the weird Go Channel Axioms[1] that you
           | have to wrap your head around to get things to work
           | correctly[2].
           | 
           | For what it's worth, I don't think Java Virtual Threads
           | (a.k.a. project loom) has implemented an explicit concept of
           | channels, although I guess BlockingQueue could work as a
           | channel.
           | 
           | There is a big issue with bringing CSP to languages like Java
           | 21 or Go, that use so-called "colorless concurrency" or
           | "stackful coroutines". CSP losses a lot of its modeling power
           | when you combin shared mutable state and pre-emptive (or
           | invisible) parallelism. And this is exactly what both of
           | these languages do.
           | 
           | "Colored" abstractions like async/await are more cumbersome
           | (especially in Rust), but they let you know exactly at which
           | points of execution your stackless coroutine may suspend, and
           | you can handle synchronization accordingly.
           | 
           | Those functional concepts are cumbersome, but threads
           | ("green", "kernel", "virtual", "goroutines" or otherwise) are
           | not the panacea their proponents claim them to be. I had to
           | debug way too many data races in my life to consider them
           | perfect. Rust still has a lot to improve on its async front
           | (no async traits, async closures or async iterators and the
           | async runtime confusion), it can at least promise me that I
           | won't get any data races.
           | 
           | [1] https://dave.cheney.net/2014/03/19/channel-axioms [2]
           | https://www.jtolio.com/2016/03/go-channels-are-bad-and-
           | you-s...
        
           | duped wrote:
           | Developers should learn the difference between concurrency
           | and parallelism because async isn't equivalent to threading.
        
             | Freedom2 wrote:
             | Yep, it's pretty alarming that a developer would toss away
             | async when threading isn't a fit for all situations - what
             | happened to the right person tool for the job? As a
             | professional, I 'hate' some things too, but I'm not going
             | to blindly make my work harder and less efficient out of
             | some small preference I have.
        
               | tomjakubowski wrote:
               | async has fragmented the crates ecosystem. If you want to
               | want to write async-free code with threads, you'll
               | probably still have to opt-in to an async executor for
               | some dependency because these days many libraries will
               | implement only an async interface
        
               | duped wrote:
               | I think you're manifesting what I'm talking about. The
               | reason you cannot replace async with threads has nothing
               | to do with the ecosystem, but the simple fact that
               | threading is a model of parallelism and async/await is a
               | model of concurrency. These are two subtly different
               | concepts in software architecture, but they are not the
               | same thing.
               | 
               | More concretely, threaded code has nothing to say about
               | whether or not its synchronous or asynchronous, and
               | asynchronous code has nothing to say about whether or not
               | it's evaluated in parallel.
        
               | fzeindl wrote:
               | > I think you're manifesting what I'm talking about. The
               | reason you cannot replace async with threads has nothing
               | to do with the ecosystem, but the simple fact that
               | threading is a model of parallelism and async/await is a
               | model of concurrency.
               | 
               | That is an artificial distinction you made up. I have
               | written many apps and desktop applications, all of which
               | where using 3-10 threads, some of them doing "parallel"
               | work meaning the parallel computation of divide and
               | conquer algorithms, some were doing concurrent work.
               | 
               | I tried async often. I threw it away every single time,
               | because it "infects" the entire codebase. I suspect the
               | only reason async exists is because the Javascript event-
               | loop is single-threaded and there are no blocking
               | primitives in JS. If you don't have access to blocking
               | data-handoff between threads, you can choose between
               | callback hell or async/await.
               | 
               | Please take a look at the video I posted earlier.
        
               | magicalhippo wrote:
               | https://towardsdatascience.com/concurrency-and-
               | parallelism-w...
               | 
               | Besides IO Completion Ports for async IO has been the
               | fastest way to do heavy IO on Windows, and it was
               | introduced years before JavaScript even was a thing.
               | 
               | And that's just one example. So hardly think JavaScript
               | was the main driver there.
        
               | singingboyo wrote:
               | It's not an artificial distinction at all, but it is
               | subtle.
               | 
               | 2, 4, even 256 sockets exchanging data can be worked with
               | concurrently on a single thread and gain performance vs
               | blocking and waiting for the first socket to finish.
               | There's no parallelism, since they're never actively
               | reading/writing at the same time, but they're concurrent
               | because they exist and operate in overlapping timeframes.
               | 
               | Running two independent algorithms could mimic this - run
               | part of algo A, then part of B, then A, etc. It's not
               | useful for performance, though. To be useful you require
               | parallelism - you have to have the algorithms executing
               | at the same time, using multiple threads.
               | 
               | On async itself - it's not perfect, but honestly, there
               | are contexts where it makes a lot of sense. I work on a
               | lot of non-blocking C code - the entire programs are
               | basically epoll and timer driven. As a result, the high
               | level coordination is just callback hell. Async is very
               | nice in comparison.
               | 
               | Yes, you can spin up threads to do everything with
               | blocking, but non-blocking I/O came around specifically
               | because the threads add overhead and kind of suck. It's
               | worth noting that having threads can also infect the
               | codebase. You either have to carefully manage mutexes, or
               | you only communicate with channels and have to worry
               | about keeping things updated and in sync. Sometimes this
               | works great with minimal communication between threads.
               | However, if you have one socket per thread and the
               | sockets are all triggering actions that mess with the
               | same data... it's not so great.
        
             | deltaninenine wrote:
             | [dead]
        
         | davedx wrote:
         | That being said... python had a BDFL and look how that turned
         | out.
         | 
         | I think designing and evolving any living programming language
         | is just one of the hardest problems out there.
         | 
         | Incredible blog post indeed, was awesome to read it.
        
           | bazoom42 wrote:
           | > That being said... python had a BDFL and look how that
           | turned out.
           | 
           | One of the most popular and succesful languages, and a major
           | force in AI innovation?
        
             | _joel wrote:
             | With a dodgy 2->3 migration pathway and far too many
             | package managers.
        
               | 015a wrote:
               | And _despite_ that, still a top 3 most popular
               | programming language on the planet. And another in that
               | top 3 is the universally-derided Javascript.
               | 
               | There's a lesson in there to learn. Its unfortunate that
               | the people who need to learn it most likely won't.
        
               | LordDragonfang wrote:
               | It's important to note that JS has an unfair advantage in
               | that if you want to write code that runs on the universal
               | platform, you're forced to use either it or something
               | that transpiles to it. Python reached where it was on
               | merits alone.
               | 
               | (That said, JS is actually a very versatile and almost-
               | great language, getting better all the time)
        
               | timschmidt wrote:
               | Python reached where it is because university CS programs
               | which had previously been teaching Java reached for a new
               | language.
        
             | viscanti wrote:
             | The py2 to py3 transition seems to have gone very poorly,
             | although most of the pain from that is behind us now.
        
               | jefftk wrote:
               | That was a seriously bad call, but it was a single one
               | and he's made a ton of other decisions over the years.
        
               | rirze wrote:
               | who's "he"?
        
               | wez470 wrote:
               | https://en.m.wikipedia.org/wiki/Guido_van_Rossum
        
               | estebank wrote:
               | Guido van Rossum
        
               | thiht wrote:
               | Honestly I blame the community.
               | 
               | The transition was slow (Python 2 was supported in
               | parallel to 3 for a long period of time so there was more
               | than enough time to migrate), relatively easy to do, and
               | brought huge benefits to the ecosystem.
               | 
               | It could have been done and forgotten in 2 years if some
               | community members had not been dicks about it.
        
               | viscanti wrote:
               | > Honestly I blame the community.
               | 
               | Sure. The point is that a community can make things go
               | poorly, with or without a BDFL.
        
               | svieira wrote:
               | It really seemed that the issue was mostly "Unicode is
               | really hard if you haven't thought _deeply_ about it"
               | because application code, library and framework code,
               | _and_ "system" Python code had to go through several
               | iterations of "here's how to do it right" before
               | something reasonable came out that solved issues for most
               | / all stakeholders. And the Swift / Raku people are still
               | sitting on the sidelines smiling, knowing that there are
               | a lot more "lenses" to view text through that Python
               | doesn't make easy (but neither does Haskell, JS, or Java,
               | so there's good company in good-enough-for-now land).
        
               | hajile wrote:
               | I blame a lack of tools.
               | 
               | JS devs have been transpiling for years now and it works.
               | 
               | Python should have released a conversion tool that could
               | automatically convert most stuff and identify the stuff
               | it couldn't convert. This kind of directed upgrade would
               | have made the process much easier for developers to
               | actually accomplish.
        
               | LordDragonfang wrote:
               | I'm confused, because isn't that exactly what they did?
               | What was wrong with 2to3?
               | 
               | https://docs.python.org/3/library/2to3.html
        
               | hajile wrote:
               | It didn't work well at all in my experience. Given how
               | much code was thrown out rather than ported, I'm guessing
               | that I'm not alone.
        
               | imtringued wrote:
               | Most of the breaking changes did not justify upgrading.
        
               | collinmanderson wrote:
               | > The transition was slow
               | 
               | Here's the timeline the way I think about it:
               | 
               | - 2008-2012 Python 3 becoming usable (byte formatting,
               | six library)
               | 
               | - 2012-2016 Libraries (Django, etc) becoming compatible
               | 
               | - 2016-today Applications (Trac, Ansible, Chrome, etc)
               | becoming compatible.
               | 
               | It took a lot of time for the ecosystem to be ready. Yes,
               | there was a lot of stubbornness, but that's reality.
               | 
               | There are many things that could have been done to make
               | the transition go smoother.
        
           | sph wrote:
           | A dictator might just have terrible ideas.
           | 
           | His dislike for functional constructs didn't do Python any
           | favour, but now that he's gone I see Python is adding the
           | kitchen sink as well. I haven't kept up with the language
           | since that pattern matching proposal.
        
             | pclmulqdq wrote:
             | People who are deeply into programming languages seem to
             | like FP too much. That and theories of type systems. Python
             | is doing some great things, but also just fucking around a
             | lot.
        
               | imtringued wrote:
               | As someone who doesn't like Python. Python's problems
               | have almost nothing to do the lack of functional
               | programming. It does have the worst lambda syntax of any
               | programming language but that is about it.
        
         | mananaysiempre wrote:
         | > [Async/await] feels so complicated and hacky, a mediocre very
         | high level concept that someone managed to implement as a zero-
         | cost abstraction.
         | 
         | I feel like I need to point out that Donald "Structured
         | programming with GO TO statements" Knuth included an example of
         | coroutines in the first volume of _The art of computer
         | programming_ , the first edition, dated 1968. In assembly
         | language for an accumulator machine. With a box of
         | scraps!^W^W^W^W^WThat is to say, that C and most other
         | languages have made coroutines awkward and thus virtually
         | unused in the past three decades or so does not mean they are
         | particularly novel or high-level.
         | 
         | Granted, Knuth used coroutines in a simulation and not for I/O,
         | so he did not need that much of a scheduler, but still.
        
         | logicchains wrote:
         | The Rust async story would be much nicer if they'd put in the
         | hard work up front to support higher-kinded types, as then it
         | could have a monadic async API like OCaml or Haskell. I don't
         | know anyone who's used async in both Rust and Haskell who
         | prefers the Rust approach. It'd also fix oddities like why it's
         | possible to write a function like the following in C++ but not
         | Rust:                   template<template<typename> class
         | MyContainer>         int getFirstInt(const MyContainer<int>&
         | myContainer) {           return myContainer[0];         }
         | 
         | (In Rust it's not possible to write something like
         | MyContainer<int>; only the int is allowed to be generic).
        
           | chrismorgan wrote:
           | Are you aware of _generic associated types_ , which landed in
           | 1.65 (late last year)? To a considerable extent, they're
           | Rust's answer to higher-kinded types. Less expressive in some
           | ways, but fitting Rust _way_ better, as basically a
           | relaxation of a former restriction, rather than a new
           | feature. If I'm reading your snippet right, GATs let you
           | express exactly that, though minus `template`'s duck-typiness
           | so you'd have to spell out what contract the container must
           | adhere to.
        
             | logicchains wrote:
             | >less expressive in some ways, but fitting Rust way better,
             | as basically a relaxation of a former restriction, rather
             | than a new feature
             | 
             | They're not expressive enough to implement a monadic async
             | library, as far as I'm aware, so they don't fulfill the
             | role of proper HKTs in this context.
             | 
             | >minus `template`'s duck-typiness so you'd have to spell
             | out what contract the container must adhere to.
             | 
             | Do you think it's consistent that the T in MyContainer<T>
             | can be "duck typed" but not the MyContainer?
        
               | zozbot234 wrote:
               | > They're not expressive enough to implement a monadic
               | async library, as far as I'm aware
               | 
               | AIUI you need GC in order to do that. There's a reason
               | why Rust's async/await support needs compiler magic.
               | 
               | (I'd like to see standard interfaces for "pluggable"
               | garbage-collected arenas in Rust, but this will need to
               | wait until after Allocators/Storages get fully
               | stabilized.)
        
               | estebank wrote:
               | In Rust neither is duck typed. For T you must specify the
               | traits it must have. This makes compilation fail at
               | definition, not at the point of incorrect usage.
               | 
               | For an example of GATs, you can see the following:
               | 
               | https://play.rust-
               | lang.org/?version=stable&mode=debug&editio...
        
         | TurboHaskal wrote:
         | > I too would have loved something more towards ML than C++
         | 
         | I stopped paying attention to the language around 2012-2013 or
         | so when the direction clearly steered towards the latter.
         | 
         | You may want to check https://austral-lang.org/ out.
        
           | bpbp-mango wrote:
           | null type :(
        
           | dorian-graph wrote:
           | I was about to ask if there's any "Rust by ML" languages out
           | there. Thank you.
        
             | noelwelsh wrote:
             | O'Caml is heading in that direction. Jane Street are adding
             | more control over memory layout and allocation, and
             | multicore finally arrived recently.
        
           | TylerE wrote:
           | Nim is the closest to what I had originally hoped Rust would
           | be. Especially Nim 2 with the new GC is really exciting. I
           | suspect it will outperform all but the best hand tuned
           | allocators.
           | 
           | It's even hard real time capable, which makes it potentially
           | viable in scenarios like games where GC traditionally hasn't
           | been.
        
         | osigurdson wrote:
         | I think a big part of any successful endeavour is alignment of
         | authority, responsibility and competence. The problem with
         | group decision making is responsibility is very diluted. If a
         | bad decision is made, no one in particular is responsible.
        
       | hardwaregeek wrote:
       | A lot of Graydon's ideas feel like interesting extensions to ML-
       | style languages. I bet if he had continued down that path, it
       | would have been a lot more of an experimental language with a
       | hodgepodge of different ideas. Which is totally valid (you need
       | these languages to test new paradigms and features), but
       | definitely would not have become mainstream.
       | 
       | Basically, I view Grayson as a leader who set the tone for Rust
       | being a language that was willing to take ambitious swings on
       | cutting edge features. But I don't think he would have been the
       | person to eventually make the cuts and compromises necessary to
       | hew the language into a cohesive, mainstream language. Rust
       | ending up as a replacement C++ helped it not only determine which
       | features to keep and which rules to follow, but also helped it
       | create the right pitch for developers to use it.
       | 
       | This does lead to a larger question about BDFLs. Perhaps, like
       | CEOs, the BDFL you want when you're starting a language is not
       | the BDFL you want when you're maturing a language, or maintaining
       | a language. Especially around feature selection, in the beginning
       | it may pay off to add a lot of features based on user feedback,
       | but later on it may be better to push back more. And from a
       | psychological standpoint, I have wondered about the pressure of
       | being a BDFL. Grayson has been open about stepping down partially
       | due to reaching his limits, and I suspect other BDFLs have
       | thought about it too. The job sounds exhausting and thankless. At
       | a certain point, wouldn't you want to leave and start a new
       | project? And wouldn't we want the person who had success once to
       | give it another shot?
        
         | dataflow wrote:
         | > Rust ending up as a replacement C++ helped it
         | 
         | If anything, it's more a C replacement than a C++ replacement.
         | It will take some market share from both of course (and other
         | languages to a lesser extent too), but functionality-wise, it
         | just isn't (currently, at least) practically able to replace
         | some C++ use cases.
        
           | 59nadir wrote:
           | In some ways I think Rust is a bad replacement for both. It's
           | an alternative in some ways to C and an alternative in some
           | ways to C++.
           | 
           | I don't think anyone who still uses C today and hasn't lived
           | in a cave for the last 20 years would be very interested in
           | Rust since it's just not at all like using C. There is the
           | rare Bryan Cantrill who for some reason was seemingly unaware
           | that other languages existed from 1990 until 2018 but I think
           | it's safe to say most other people who primarily use C would
           | not prefer the leap to something like Rust.
           | 
           | For the people who use C++, certainly they're already used to
           | a language that wants to dominate, so Rust should be fine. In
           | terms of features, apparently it doesn't hold up, but because
           | it's like C++ I'm sure they're just a couple of years away
           | from adding those things too, and then the language can
           | continue being more important than the actual data
           | transformations the programs are supposed to be doing.
        
             | dataflow wrote:
             | Yeah to some extent I agree. Regarding this:
             | 
             | > but because it's like C++ I'm sure they're just a couple
             | of years away from adding those things too
             | 
             | If they're smart (which they are), I'm sure they'll
             | eventually cave and add some of the missing stuff, no
             | matter how much they want to believe these features are
             | unnecessary. Just like how C is finally coming around and
             | adding generics and all that. The particular capabilities I
             | mentioned here wouldn't be impossible to add, and they can
             | probably achieve some of them better than C++ did. But I do
             | think there will remain use cases that Rust will fail to
             | accommodate.
        
           | badrequest wrote:
           | I'm interested in more on this. What can I do in C++ that I
           | can't do in Rust?
        
             | TremendousJudge wrote:
             | Write unsafe code wrapped in layers of those advanced
             | features that half the team didn't even know existed, and
             | push it into production.
        
               | vanderZwan wrote:
               | Am I the only one who works at a place where things don't
               | make it into production unless at least one other person
               | on the team fully understands the code during review?
        
               | TremendousJudge wrote:
               | Plenty of people don't, it's the most common thing. Maybe
               | right now you are lucky enough to work in good contitions
               | (that's great!), but are you sure it couldn't happen to
               | you in 10 years? I think the standard tools we use in our
               | profession should work in less than ideal conditions as
               | well.
        
             | dataflow wrote:
             | This is just one example, but moves are always bitwise,
             | correct? So if I want an object to track all users of it in
             | C++, I can just make a smart pointer for it whose
             | copy/move/destruction operations notify the object about
             | each event. How would you do that in Rust? (Similarly, what
             | if I want a relative pointer?)
        
               | remram wrote:
               | You can track all of this (things like Rc or Arc do)
               | except move.
               | 
               | Relative pointers are possible, depending on what you
               | mean. Making this safe (e.g. preventing users of that
               | type from breaking the relative addressing) is done via
               | the Pin type.
        
               | dataflow wrote:
               | > You can track all of this (things like Rc or Arc do)
               | except move.
               | 
               | Which means you can't track this. Tracking moves is
               | fundamental here.
               | 
               | > Relative pointers are possible, depending on what you
               | mean.
               | 
               | Pretty sure they're not possible in the sense I mean, for
               | the same reason as above - you need custom moves for
               | this. I'm referring to a pointer (not an offset; a
               | pointer) that automatically adjusts itself when copied or
               | moved. So that it always points somewhere N blocks
               | before/after itself.
               | 
               | These are just two examples, to get the point across that
               | Rust actually lacks some capabilities (since somehow that
               | surprises people). You can find more.
        
               | remram wrote:
               | > Tracking moves is fundamental here.
               | 
               | I mean, arguably. If it is fundamental for your
               | hypothetical use case, then sure, but this is not
               | required for a lot of use cases, like smart pointers.
        
               | dataflow wrote:
               | > this is not required for a lot of use cases, like smart
               | pointers.
               | 
               | Nobody claimed otherwise. The question was what things
               | Rust can't do, not what it can do.
        
             | steveklabnik wrote:
             | (Not your parent) Many people point to more advanced meta
             | programming, such as variadrics, higher kinded types
             | (though as mentioned GATs advance Rust in this general
             | area), and specialization.
        
       | jstx1 wrote:
       | A bunch of things you don't like about Rust? Turns out that the
       | person who originally created the language doesn't like them
       | either.
       | 
       | I know that the main point is about governance and how having a
       | BDFL would have led to a completely different language but I
       | really would have preferred the Graydon-BDFL-Rust to what we have
       | today.
       | 
       | Very interesting article, worth a read.
        
         | lamontcg wrote:
         | I think I mostly want a language like Graydon-Rust, but not
         | with the Expressivity tradeoff (or at least not as hardcore as
         | it sounds like Graydon is).
        
         | shoulderfake wrote:
         | [dead]
        
         | avgcorrection wrote:
         | > A bunch of things you don't like about Rust? Turns out that
         | the person who originally created the language doesn't like
         | them either.
         | 
         | Yep. That just goes to show that you shouldn't put individuals
         | on pedestals as if they are superheroes. More things than we
         | usually think are in fact team efforts.
         | 
         | > I know that the main point is about governance and how having
         | a BDFL would have led to a completely different language
         | 
         | That isn't the only _main point_.
         | 
         | > > The Rust I Wanted probably had no future, or at least not
         | one anywhere near as good as The Rust We Got.
         | 
         | If you would be fine with a niche language then that "Rust"
         | would have worked for you. But if you also wanted a language
         | with wide industry backin etc.--maybe not so much.
        
         | elcritch wrote:
         | One highlight from the article:
         | 
         | > Traits. I generally don't like traits / typeclasses. To my
         | eyes they're too type-directed, too global, too much like
         | programming and debugging a distributed set of invisible
         | inference rules, and produce too much coupling between
         | libraries in terms of what is or isn't allowed to maintain
         | coherence.
         | 
         | Very much this!
         | 
         | > I wanted (and got part way into building) a first class
         | module system in the ML tradition. Many team members objected
         | because these systems are more verbose, often painfully so, and
         | I lost the argument. But I still don't like the result, and I
         | would probably have backed the experiment out "if I'd been
         | BDFL".
         | 
         | I seen this the ML first class module system mentioned in a few
         | different contexts and want to pick it up at some point.
        
           | wuiheerfoj wrote:
           | May I ask what you don't like about traits (and perhaps
           | insight on what the author meant)?
           | 
           | Being a relative rust noob compared to other languages, I
           | always felt traits were a super power when compared to eg
           | interfaces in Java/C++ and flexible but with useful
           | constraints compared to the structural typing nature of
           | Typescript interfaces
        
             | nyanpasu64 wrote:
             | Importing a trait into scope can silently materialize
             | methods on other types, with no syntax at the method call
             | site pointing to which import statement created the
             | methods, requiring you to ask the compiler/IDE. This can
             | also mean that removing a use statement with no references
             | to the type being used in the entire rest of the source
             | file, can make code suddenly stop compiling. I've worked
             | around this by strictly confining "specialized" traits
             | (like std::io::Write or std::fmt::Write, why are there two
             | Writes?!) to the beginning of a single function, but I'm
             | lazy and put "general" traits like std::borrow::Borrow and
             | std::str::FromStr at the top of my file.
             | 
             | Which types grow new trait methods is dependent on trait
             | implementations, which can be generic and apply to an
             | unbounded number of types, based on complex matching rules,
             | and figuring out requires reading every impl of that trait
             | to see if any match a given type, or asking the
             | compiler/IDE. I want to explore languages which explicitly
             | select a trait implementation at the call site (like a
             | Heap<int, int_greater> type which uses int_greater::cmp()
             | to implement a min/max heap), or naming the trait (but not
             | picking an impl) at the trait method call site (like
             | Write::write_all(stream, "hello world") or (stream as
             | Write).write_all("hello world")). I think Zig takes a
             | similar direction.
        
               | zozbot234 wrote:
               | You can use universal function call syntax in Rust:
               | <Type<TyArg> as Trait>::trait_fn(self_arg, fn_arg1,
               | fn_arg2);
        
               | glandium wrote:
               | There's worse. When your trait adds function foo and your
               | code does obj.foo(), if the underlying type later adds a
               | foo method, compilation breaks. This is very common with
               | traits that usefully add useful methods that are missing
               | in libstd... which break when said method is finally
               | added there.
        
               | Gwypaas wrote:
               | I ran into this case yesterday, for some cases there are
               | warnings now. In this case I used ".div_ceil(...)" from
               | from the Num [0] crate.
               | (some_integer).div_ceil(&2)
               | ^^^^^^^^              = warning: once this associated
               | item is added to the standard library, the ambiguity may
               | cause an error or change in  behavior!         = note:
               | for more information, see issue #48919
               | <https://github.com/rust-lang/rust/issues/48919>
               | = help: call with fully qualified syntax
               | `num::Integer::div_ceil(...)` to keep using the current
               | method         = note:
               | `#[warn(unstable_name_collisions)]` on by default
               | 
               | [0]: https://crates.io/crates/num
        
               | glandium wrote:
               | This warning happens during the time the method is added
               | to libstd but is still unstable.
        
               | Gwypaas wrote:
               | Given that all important codebases should have CI that
               | rejects warnings and runs clippy I would say it is
               | acceptable.
        
             | sanxiyn wrote:
             | In a trait-like system (including Java interface), there
             | can be only one implementation of a trait for a type.
             | That's why it is global, unlike ML module-like systems.
             | Global is another word for anti-modular.
             | 
             | For example, in the real world, there are multiple ways to
             | order strings (called collation), but since there can be
             | only one implementation of (String, Ord) type-trait pair,
             | one ordering is canonical. That may be bearable, but having
             | canonical (String, Hash) implementation is not. What if you
             | want to use (say) faster CityHash instead of canonical
             | MurmurHash? So Rust resorts to things like BuildHasher, to
             | get back multiple implementations.
             | 
             | Because it is global/anti-modular, it interferes with
             | separate compilation, and it is one of reasons why Rust is
             | slow to compile.
             | 
             | Then why would one use trait instead of module? Since there
             | can be multiple implementations in module, you need to
             | specify. So module is more verbose. In my (and Graydon's)
             | opinion, a bit more verbosity is worth it for modularity,
             | but many people disagreed.
             | 
             | This is another theme: Graydon is okay with verbosity,
             | boilerplate, and being bureaucratic. In my (and Graydon's)
             | opinion, programming is work that is secretarial, not
             | artistic, so it is unimportant whether code is ugly or not.
             | You may think current Rust is ugly, but no, it is the way
             | it is because lots of people really cared about Rust code
             | being pretty. If Graydon was a BDFL, Rust would be even
             | more ugly, and in my opinion, as a result, would be a
             | better programming language.
        
           | dan-robertson wrote:
           | I often felt that being canonical was a pretty big advantage
           | of traits. For example if you use first-class modules then
           | every time you want a sort or a binary search tree you need
           | to specify the choice of comparison operator for the
           | elements. And a data structure for a search tree must somehow
           | encode this in the type system so eg you can't merge two
           | trees with the same key type but different comparison
           | functions. The most obvious way to implement this causes
           | problems for other cases like writing functions generic in
           | the types of keys/values (else you get extremely verbose
           | code).
           | 
           | Canonical instances goes against modularity but something
           | like modular implicits for ML-family languages could improve
           | the terse was a lot to something closer to Haskell levels.
           | You still miss out on the other advantage rust has: the dot
           | operator is great because it allows identifiers to contain
           | less context (they don't need to have long global names or be
           | hidden away in some tree of modules: you can have .map mean
           | different things for different types) and gives a massive
           | hint to autocomplete for which things to suggest (I don't
           | know how you even signal to autocomplete in a language like
           | ML that you would like a function to operate on the following
           | value so please suggest things with the right type).
        
             | zozbot234 wrote:
             | Most type systems are not full-featured enough to elegantly
             | encode traits/typeclasses or ML modules. You need dependent
             | types, as seen in languages like Agda or Idris. And these
             | in turn are hard to implement in a language with a
             | traditional compile/run phase distinction like Rust: the
             | _full_ feature-set of dependent types is pretty much only
             | available at compile time. (Which is why dependent-typed
             | languages tend to add "program extraction" features, which
             | reintroduce that phase separation in a rather ad-hoc way.)
        
           | giraffe_lady wrote:
           | I loved reading that part! I've always found ocaml's (and now
           | rescript's) first class modules flexible and powerful.
           | They're definitely awkward but in that way where you're
           | having to explicitly think about and declare relationships
           | you'd happily ignore until they bite you.
           | 
           | Recently I've seen a lot of programmers I respect consider
           | them a weaker or failed alternative to typeclasses, basically
           | a dead-end. And I had been reluctantly coming to the
           | conclusion that I must be wrong in some way I'm not able to
           | fully perceive yet. Seeing such a notable PL designer come
           | out on their side makes me feel less like a fool.
        
           | eru wrote:
           | Having used both OCaml and Haskell in production, I can say
           | that the typeclass way of doing things is massively more
           | convenient.
           | 
           | But I guess enough syntactic sugar might make things
           | palatable.
        
         | skellington wrote:
         | Committees tend to design less beautiful things. Look at modern
         | c++ too. What a mess. Everyone in the committee has to jam in
         | their favorite feature, much of which is sugar.
        
           | eru wrote:
           | Haskell is designed by committee, and it's a fairly alright
           | language.
        
             | tinco wrote:
             | It suffers from the same problem C++ does though, it's got
             | a broken "prelude" and the committee just doesn't have the
             | courage to ever fix it. Weirdly a long time ago the opinion
             | was that since the language was academic they weren't
             | hindered by the demands of stability that corporate users
             | might so they could do epic things like the switch to
             | monadic I/O.
             | 
             | I feel if the Haskell committee could simply pick one of
             | the alternative preludes, and gather some packages into a
             | new standard library, and announce it all as a Haskell 2.0
             | or Haskell prime or whatever it could really reinvigorate
             | the community. But what committee would be motivated to do
             | such a thing, bearing the responsibility if it went wrong?
        
               | marcosdumay wrote:
               | They _are_ fixing it... it 's just happening at committee
               | speeds.
               | 
               | I would really like to be able to specify what prelude I
               | want at the .cabal file. AFAIK, that's one change that
               | wouldn't break anything.
               | 
               | (Anyway, Haskell is one case of a language asking for a
               | fork. Maybe it takes the form of Idris or some other
               | derived language having some sudden growth instead, but
               | you are right, the committee currently isn't as fast as
               | the community.)
        
             | asplake wrote:
             | Modulo which prelude you're supposed use. That was enough
             | to put this newcomer off
        
           | nsajko wrote:
           | Modern C++ is actually going relatively OK? You may be
           | thinking of the old, pre-C++11 C++?
        
             | chippiewill wrote:
             | Modern C++ _is_ actually a mess to be fair. Overall it's
             | still an improvement on pre-c++11, but when looking at it
             | end-to-end it feels awfully disjointed.
        
             | Sankozi wrote:
             | If you have a mess and add something to it, you still have
             | a mess, probably a bigger one. To clean the mess you need
             | to remove something (which is really hard in a programming
             | language) not to add something.
             | 
             | Selecting and enforcing usage of a subset of C++ is a way
             | to deal with that mess which companies frequently use.
        
               | nsajko wrote:
               | Everything is a mess. The universe is a mess. That
               | doesn't make perfectionism a good thing.
               | 
               | Besides, the C++ committee does, in fact, remove stuff
               | from the language to clean the mess, just not without due
               | care.
        
               | rcxdude wrote:
               | They do so once in a blue moon, and only for ideas so
               | spectacularly bad that either they were never implemented
               | or never used by anyone. Those aren't the main problem
               | with C++ mess, the issue is the many small but important
               | details which are deeply embedded into the language since
               | it started as a super-set of C.
        
               | zozbot234 wrote:
               | > To clean the mess you need to remove something
               | 
               | Sure, but that's why Carbon and Cppfront are a thing now.
               | These two actively remove stuff from C++.
               | 
               | We'll probably see this happen in the Rust community as
               | well once the newer Crab language becomes established.
               | Then we'll have to rewrite everything in Crab, but it
               | will probably be a lot simpler than the Rust rewrite.
        
               | [deleted]
        
             | flohofwoe wrote:
             | C++11 is exactly where the mess started ;) C++ releases got
             | more frequent since then, but at the same time, more half-
             | baked features slip in that haven't been battle tested in
             | the real world before, or need refinement over the next
             | decade worth of releases (if they can be fixed at all).
        
             | rcxdude wrote:
             | I don't think that modern C++ is any less messy than old
             | C++. If anything it's more so, as even more difficult
             | conscessions to backwards compatibility emerge. That's not
             | the say the new functionality isn't useful, but I don't
             | think it cleans anything up. Modern C++ is approaching an
             | order of magnitude more complexity than pre-C++11 (which is
             | not surprising if you are adding features but not removing
             | or changing old functionality), and I don't think it's a
             | sustainable increase.
        
         | crispinb wrote:
         | > Turns out that the person who originally created the language
         | doesn't like them either.
         | 
         | But it also turns out the very same isn't as neurotically
         | attached to his 'likes' as most of us are:
         | 
         | > The Rust I Wanted probably had no future, or at least not one
         | anywhere near as good as The Rust We Got. The fact that there
         | was any path that achieved the level of success the language
         | has seen so far is frankly miraculous. Don't jinx it by
         | imagining I would have done any better!
         | 
         | He understands that his likes are just contingent facts about a
         | single mammal, not truths. This is a hard-won understanding.
         | Most people never reach it.
        
         | rtpg wrote:
         | I am very glad to not have Rust end up along that path (I am
         | sympathetic to the threading idea he mentioned though).
         | 
         | Rust as an alternative to C++ does, for me, involve all of the
         | weird magical nonsense that you kind of need to get any of this
         | working. The extreme use of generics to build out DSLs to get
         | things working. General libraries being very hard to write, but
         | still possible, to get alright ergonomics for usage itself. And
         | yeah... the zero-cost abstraction thing.
         | 
         | I think Graydon-Rust would have also been very interesting, but
         | it sounds unappealing to me, person who wants "C++ but nicer".
         | 
         | But to his point... saying "you could have much faster compile
         | times" is very tempting! Just, especially when it's messing
         | around with Rust "for fun", the ergonomics sound pretty unfun.
        
           | xiphias2 wrote:
           | Some Rust developer said that Rust was originally what Go has
           | become.
           | 
           | So if you want to try something like that, just look at Go :)
        
             | hajile wrote:
             | Go is actually a competitor with StandardML and loses badly
             | in every way except having Google's deep pockets to carry
             | it.
             | 
             | StandardML uses a far superior Hindley-Milner type system.
             | It has pattern matching. It has Option types for good error
             | handling. It doesn't have bad features (for GC'd languages)
             | like direct pointers and slices. It has immutability by
             | default. CML even offers a better take on channels too.
             | Modules keep interfaces more standardized (superior for big
             | projects IMO). And to top it all off, SML is easier to
             | learn than Go. It's also as fast as Go (despite the
             | compilers being a side project).
             | 
             | Go's only advantage is more extensive libraries, but that
             | would be fixable in SML with just a fraction of the money
             | Google spent on Go.
        
       | bfrog wrote:
       | After reading this I'm really quite happy Graydon created Rust,
       | but then conceded the path it has taken.
       | 
       | It really is an incredibly language and ecosystem, in large part,
       | because of its performance potential.
       | 
       | To be clear, the only real options in this space were arguably C,
       | C++, and maybe in some circles D in my mind. C++ and C by far had
       | the mind share.
       | 
       | Had Rust gone the way Graydon wanted I don't think Rust would be
       | so interesting in the OS and Embedded space. This is a space that
       | it turns out is really ripe for change.
       | 
       | Embedded application are growing more connected, and more complex
       | all the time. Security is a serious concern perhaps followed by
       | or proceeded by performance depending on who you ask. Rust checks
       | so many boxes off in this space its really hard to argue that it
       | isn't a better solution.
       | 
       | Would you rather write a little embedded http server on an IoT
       | device in C, C++, or Rust? What about an embedded networking
       | stack? What about a mesh network stack? I know the answer I'd
       | have every time for this myself.
        
         | fallat wrote:
         | Zig
        
       | demarq wrote:
       | beautiful ending!
        
       | avgcorrection wrote:
       | I haven't cared about what Hoare thinks of Rust since 2015.
       | 
       | 1. He hasn't been involved in the language for a long time
       | 
       | 2. His vision for the language was completely different compared
       | to how the remaining developers ended up designing it. So if I
       | ended up liking Rust under his "BDFL"ing then it would be for
       | completely different reasons compared to why I like (and dislike)
       | Rust today
        
         | avgcorrection wrote:
         | Hmm, I had only read a few paragraphs when I wrote that. Now
         | I've read through the whole thing and it seems that Hoare
         | agrees with me. How nice.
        
       | svieira wrote:
       | > Not 100% clear about actors -- I was weirdly focused on that
       | model that in practice has many issues
       | 
       | Does anyone have any insights on the particular issues with
       | actor-based concurrency vs. "direct parallelism like threads or
       | locks" that he might be thinking about here?
        
       | codedokode wrote:
       | I don't understand why in Rust integers wrap around. It doesn't
       | make sense and there are vulnerabilities caused by wrapping
       | integers in C.
        
         | sdfghswe wrote:
         | What's the alternative? What would you expect happens?
        
       | p0nce wrote:
       | > First-class &
       | 
       | Interestingly like Graydon suggests this and 'tis something D
       | has, you can only have `ref` for function parameters. This is
       | something the users sometimes complain about, but I guess it has
       | positives.
        
         | FeepingCreature wrote:
         | Yeah a lot of these features sound like he wants "D with
         | lifetimes".
        
       | LeanderK wrote:
       | Can someone comment on "Library-defined containers, iteration and
       | smart pointers"? I have no rust experience so far. Magic compiler
       | support for such primitives is something I usually very much,
       | really strongly dislike, it was always my experience that it is
       | the wrong point of abstraction because it just reduces the design
       | state so much. But then you need good support for inlining the
       | relevant parts of the language, which I think is very often very
       | poorly done. In fact, I have so far never seen a solution I like.
       | 
       | What is the rust experience wrt to inlining? Can every expression
       | be inlined or only selected ones? How can you know what got
       | inlined in some expression? Do you have to manually annotate
       | every single function call you have to inline or is there a more
       | general command?
        
         | tijsvd wrote:
         | Everything that's visible to the compiler is subject to
         | automatic inlining. That is all code in the current crate
         | (compilation unit), all concrete instantiations of generics
         | (regardless where defined), and all functions marked inline
         | (regardless which crate).
         | 
         | Stdlib containers are all in the generics category.
        
           | LeanderK wrote:
           | > all functions marked inline (regardless which crate).
           | 
           | Is this a problem is rust? Is too much/too little marked
           | inline? What if you really need to inline some function from
           | some library that was not marked inline by the author?
        
         | JonChesterfield wrote:
         | If containers, control flow and so forth are expressible as
         | library code you get a simpler compiler and stuff determined
         | users can reasonably debug, modify, replace. It means you have
         | to improve the core language enough to implement them and/or
         | have magic compiler intrinsics which are only intended for use
         | by that library.
         | 
         | If you implement these things in the compiler (open code means
         | emit the implementation inline as you go, can also emit calls
         | to the compiler runtime which is roughly similar to library
         | code that the compiler ships and knows lots about) then users
         | need to hack the compiler to change them.
         | 
         | However, if the structures are in the compiler, and you've done
         | things like encode them directly in the AST, the compiler has a
         | better chance of emitting useful diagnostics for them and of
         | optimising them at the semantic level of the container.
         | 
         | C++ goes with library code supported by compiler intrinsics,
         | and a common developer experience is compilation errors
         | referring to iterators some distance into the library code. It
         | also can't sanely do things like call reserve on a vector
         | outside of a loop, because by the time it's ready to optimise
         | things it's holding raw pointers with mangled names, not a
         | hashmap instance.
         | 
         | Conventional wisdom is to put containers in the stdlib. D has
         | some support in the compiler. I'm starting to think this is one
         | where conventional wisdom has got it wrong.
        
       | moonchrome wrote:
       | As someone casually following Rust and haven't touched it or C++
       | in probably 5+ years (but keeping tabs in hope of coming back to
       | that world), I feel like a lot of decisions he mentions are what
       | made me optimistic about Rust adoption/positioning itself as a
       | modern C++ replacement. So I kind of agree with his conclusion -
       | his version of Rust would be much less interesting to me, and I
       | think a lot of it's current users. eg. I only started playing
       | with Rust once they removed green threads. Zero cost abstractions
       | are a major selling point.
        
         | rob74 wrote:
         | I think this quote also applies more generally:
         | 
         | > _It 's easier to work with than C++, but that's fairly faint
         | praise._
         | 
         | So, while Rust positions itself as a C++ alternative with all
         | the complexities that C++ developers love, it may become less
         | attractive for other use cases. For instance, I find it highly
         | questionable to develop a web app in Rust...
        
           | [deleted]
        
           | smabie wrote:
           | There's tons of existing languages that are mostly okay that
           | you can develop a webapp in.
           | 
           | Rust as a C++ replacement serves a real unmet need in the
           | marketplace.
        
           | devjab wrote:
           | We build a proof-of-concept backend replacement for what is
           | essentially "SharePoint being used as a DB with a frontend by
           | people who don't know how to use SharePoint" and was a nice
           | experience. We also build it in a few other languages, C#,
           | Go, Python and TypeScript and Rust was probably the best
           | experience of them all. We ended up going with C# because we
           | needed Odata, and at the time we hadn't yet run into the many
           | "joys" of working with Odata, ASP and Entity Framework and
           | how their model builders, really, really, really, won't play
           | together nicely.
           | 
           | Knowing what we know now, we should've gone with TypeScript
           | and just written our own Odata filter on top of it, but live
           | and learn.
           | 
           | If I was in a position where I could pick and chose
           | languages, and not worry about how not having TypeScript in
           | most things will mean our best front-end developer can never
           | go on vacation because he's sort of our only front-end
           | developer, I wouldn't mind using Rust for web-backends.
           | 
           | Rust GUI is obviously not a great experience. At least not
           | yet. But it's not that bad either. I think it's mostly the
           | case of how JavaScript is just so good at it and seeing such
           | a fast pace of improvements because the entire world uses it
           | for most GUIs these days, that it's just hard for anything
           | else to compete. I mean, look at stuff like Flutter or
           | Blazor, they are backed by Microsoft and Google and they're
           | vastly inferior choices for most use cases compared to simply
           | building things in React, ReactNative or even electron, and
           | that's not because I have some wild love for JavaScript, it's
           | because it's seeing rapid improvements they dwarf it's
           | competition simply by being used by a lot of people.
           | 
           | I wish Rust would have someone like Facebook pick it up and
           | build a frontend framework for it, but I think that is just
           | too unlikely for you to bet on, and you certainly wouldn't
           | want to do it yourself, even as open source because then that
           | would probably be your entire job.
           | 
           | On the flip-side, the packages that handle basic back-end web
           | stuff for Enterprise use are rock solid in Rust. Which is
           | impressive, at least to me, considering it's young age. I
           | have no idea why, but maybe some serious players are
           | contributing to it because they use it themselves. There
           | isn't a "Django" or Ruby+Rails for Rust, but if what you're
           | building is a lot of smaller APIs with various transport
           | methods and data access in an federated authentication
           | scenario then Rust is surprisingly mature for the web. It's
           | primary disadvantage being that your TypeScript and Rust
           | developers won't be able to cover for each other (which is
           | why we didn't poc with Java).
        
             | internetter wrote:
             | > I wish Rust would have someone like Facebook pick it up
             | and build a frontend framework for it
             | 
             | We have yew, and my personal favourite: leptos
             | (https://leptos.dev/)
        
               | lumb63 wrote:
               | As someone who recently started their first web app in
               | Rust and is using Yew for the front-end, I wish they
               | included benchmarks against other Rust frameworks. I also
               | wish I had more experience and had looked harder; Yew is
               | very verbose in my experience and Leptos looks a little
               | more sane.
        
           | [deleted]
        
           | tick_tock_tick wrote:
           | > it may become less attractive for other use cases. For
           | instance, I find it highly questionable to develop a web app
           | in Rust...
           | 
           | I mean who'd want to and why bother trying to support that
           | use case? Basically none of Rust's value proposition exists
           | for a web app while nearly every single one of the downsides
           | do.
        
             | inferiorhuman wrote:
             | Hard disagree. Rust brings a lot to the table for a web
             | app. Compiled vs interpreted makes it easier to catch
             | (syntax) errors beforehand. You can even go as far as
             | compiled templates. Mapping JSON (or whatever) to strongly
             | typed objects is _great_. Dependency management is best in
             | class. I rather like diesel.rs (although it is very much an
             | acquired taste) especially if you treat it like a safe
             | query builder rather than an ORM.
             | 
             | The single biggest problem I've run into is that there's no
             | real good story for a web app framework (especially since
             | everyone's gone crazy over async). Rocket is perhaps a bit
             | too magical for rust folks and it's been abandoned, but I
             | rather liked it. I've warmed up to axum, but all this async
             | stuff still rubs me the wrong way.
             | 
             | And, yes, compile times still suck.
        
               | EdwardDiego wrote:
               | > Dependency management is best in class
               | 
               | Do crates have namespaces that ownership is verified for?
        
               | geodel wrote:
               | Crate namespaces is best-in-class++ feature and currently
               | not available.
        
               | EdwardDiego wrote:
               | Yeah, I ask because the JVM world has had that since...
               | whenever Maven's central repository came about [0][1][2],
               | I guess, and it ensures that there's no debate about
               | namesquatting [3], nor typosquatting attack vectors [4].
               | 
               | I am constantly baffled that NPM, PyPi, crates.io etc.
               | didn't copy this idea for those last two reasons. In my
               | mind, it's not quite best in class without it.
               | 
               | [0]: https://central.sonatype.org/publish/requirements/co
               | ordinate...
               | 
               | [1]: https://central.sonatype.org/faq/how-to-set-txt-
               | record/
               | 
               | [2]: https://central.sonatype.org/publish/requirements/co
               | ordinate...
               | 
               | [3]: https://internals.rust-lang.org/t/pre-rfc-formal-
               | squatting-p...
               | 
               | [4]: https://blog.sonatype.com/this-week-in-malware-
               | may-13th-edit...
        
               | Gigachad wrote:
               | Arguably TypeScript is pretty close to as safe while
               | being less work.
        
               | inferiorhuman wrote:
               | Perhaps. But TS is still Javascript under the hood which
               | means implementation details leak and they smell awful.
               | If they didn't it'd come down to a preference for
               | compiled vs interpreted.
               | 
               | The number one syntactical beef I've is operators. e.g.
               | == vs ===, ""+number.
        
               | vsnf wrote:
               | Although all of those things are true, I think the main
               | point being made is why choose _Rust_ for that, instead
               | of Go, .NET, etc, any of which offer everything you 've
               | outlined as being worth having, while having fewer of the
               | downsides of Rust.
        
               | inferiorhuman wrote:
               | any of which offer everything you've outlined
               | 
               | They don't, that's the "problem". Off the top of my head
               | dependency management in Go is not best in class to put
               | it charitably. .NET will tie you more closely to Windows.
               | Sure, mono is a thing but you'll have more packages to
               | choose from and fewer compatibility issues running .NET
               | on Windows.
        
               | foobarbaz33 wrote:
               | Mono? 2000 and late called and want their .NET
               | implementation back. Now it's all about the .NET formerly
               | known as "Core".
        
               | Paradigma11 wrote:
               | Web development for .NET is multiplatform for quite some
               | years now. https://en.wikipedia.org/wiki/.NET
               | 
               | Mono is mostly only relevant if you need platform
               | specific capabilities that wont be covered in .NET.
        
               | Merad wrote:
               | Your knowledge of the .Net ecosystem is a bit out of
               | date. It's had first class support for mac and linux for
               | 7-8 years now.
        
               | scns wrote:
               | First class support on Linux and Mac with caveats. Zero
               | snark intended.
        
               | LinXitoW wrote:
               | To me, one thing missing from most popular web languages
               | is the single best part of Rust: A well-defined handling
               | of the non-happy path.
               | 
               | I'm talking about things like exhaustive "switches",
               | built-in, ENFORCED handling of "missing" values (null,
               | undefined), and finally useful error handling.
               | 
               | I actually don't need any of the baremetal features of
               | Rust. It's just that most of it's "zero-cost"
               | abstractions are still far superior to those of other
               | languages.
        
               | kaba0 wrote:
               | Haskell, Scala, F#, OCaml.
               | 
               | (Some dislike it, but Java's checked exceptions are exact
               | homologous of result types)
        
               | marcosdumay wrote:
               | The problem with Java exceptions is that they have very
               | poor generic support.
               | 
               | Outside of that, yeah they just differ in very low-level
               | aspects that most developers don't even know about.
        
               | pjc50 wrote:
               | > Compiled vs interpreted makes it easier to catch
               | (syntax) errors beforehand. ... Mapping JSON (or
               | whatever) to strongly typed objects is great
               | 
               |  _cough_ C#. You also get LINQ. And a fairly heavy amount
               | of web frameworks in ASP.NET  / Razor.
        
               | kaba0 wrote:
               | Or Java, F#, Scala, Kotlin, Haskell. If we were to
               | randomly pick a language, chances are it could be a good
               | fit for these -- Rust became as well-known as is because
               | it was made for a different niche, where there were no
               | competition.
        
               | moi2388 wrote:
               | Haskell is a beautiful language, but I find their web
               | frameworks rather cumbersome to work with personally.
        
               | EdwardDiego wrote:
               | Hell, even Python can map JSON to a strongly typed
               | object. I am a firm fan of Pydantic and its competitors
               | when dealing with JSON blobs. No-one deserves a KeyError
               | in prod code.
        
             | Vespasian wrote:
             | I am doing that for a just for fun side project and it was
             | an interesting experience.
             | 
             | The pieces and features i wanted exist but tying them
             | together was not always straight forward.
             | 
             | From the top of my head I used:
             | 
             | - actix-web
             | 
             | - tracing (logging)
             | 
             | - sea-orm
             | 
             | - config from yaml files and a custom implementation to
             | allow profiles.
             | 
             | - utoipa (for openapi and swagger ui)
             | 
             | - thiserror
             | 
             | In a professional context I would not have used Rust for
             | this project, but it was quite fun to explore it's
             | possibilities and rich crate ecosystem.
        
             | FpUser wrote:
             | >"I mean who'd want to and why bother trying to support
             | that use case"
             | 
             | I write my web apps in C++. They tend to be little bit more
             | than just query database / update database. They're exposed
             | as JSON based RPC and can be accessed by JS front end from
             | browser or third party systems we interact with. The
             | performance is stellar and the code size is not much
             | different comparatively to using PHP / Ruby / Python /
             | your_pet_goes_here.
        
               | throwaway2037 wrote:
               | Nice. Do you have some open source samples? I would like
               | to see your style!
        
               | FpUser wrote:
               | No. I do not work on opensource projects unfortunately.
        
           | moonchrome wrote:
           | >So, while Rust positions itself as a C++ alternative with
           | all the complexities that C++ developers love, it may become
           | less attractive for other use cases. For instance, I find it
           | highly questionable to develop a web app in Rust...
           | 
           | But we already have plenty of options for this use case
           | (.NET, JVM, Go, Python, Ruby, node.js, PHP, Erlang, etc.
           | etc.). Very mature ecosystems that solve a lot of different
           | edge cases.
           | 
           | There are very few C++ alternatives worth mentioning, none as
           | mature as Rust in terms of adoption/tooling.
           | 
           | If you need to write a webapp that would require C/C++ kind
           | of memory handling/performance then Rust would be the ideal
           | candidate I think.
           | 
           | I don't know what kind of tradeoff matrix makes you use the
           | Rust graydon describes over the existing options.
           | 
           | For example my main issue with D (but TBH that's something
           | I've given up on actively tracking 10 years ago probably) is
           | that by including GC in the runtime/stdlib - it basically
           | painted itself as a poorly supported competitor to C# rather
           | than a C++ replacement.
        
         | sph wrote:
         | It makes me smile that my top-level comment says I would have
         | preferred the ML-version of Rust, while you say you prefer the
         | zero-cost-abstraction version of Rust that's more C++-like.
         | 
         | Indeed in software engineering there is no silver bullet nor a
         | perfect language for everybody :-)
        
           | kaba0 wrote:
           | ML-version of Rust would not have been a uniquely interesting
           | language. It targeting the C++-niche is what made it into
           | quite the big name it has become.
        
           | unrealhoang wrote:
           | There's already a ML-version of Rust, it's Ocaml
        
             | hajile wrote:
             | I'm pretty sure that the ML version of Rust is actually
             | ATS.
             | 
             | https://www.cs.bu.edu/~hwxi/atslangweb/
        
         | kzrdude wrote:
         | I was at the sidelines when Rust 1.0 was being made and I think
         | it got into an llvm induced feedback loop. Slowly turning into
         | C or C++ with other features but the same type, object and
         | memory model.
         | 
         | Part of the reason was Rust's desire to show itself as a direct
         | competitor w.r.t performance, I think.
        
           | Ygg2 wrote:
           | Performance, but with sanity.
           | 
           | Say you use a vector in C++. You push one element and pop
           | two. In Rust that's a None. In C++ the answer is UB (in my
           | case 43).
        
             | pjmlp wrote:
             | In C++ it is an error, if one bothers to enable bounds
             | checking.
             | 
             | https://godbolt.org/z/vYcMhE9h7
             | 
             | > Error: attempt to access an element in an empty
             | container.
        
               | Ygg2 wrote:
               | It's not an error by default and more importantly it's
               | not an error under right circumstances (e.g. lets say
               | number of pops and inserts is supplied via arguments).
        
               | pjmlp wrote:
               | Yes defaults matter, it doesn't change the fact that
               | protective gear is available, and like seatbelts, helmets
               | and motorrad full body armour, it is up for security
               | conscious people to make do of what is made available to
               | them.
               | 
               | As for your dynamic arguments request, have fun,
               | 
               | https://godbolt.org/z/YYKrnh4Y9
        
               | Ygg2 wrote:
               | It's not just that defaults matter, the problem here is
               | why have footgun as the default?
               | 
               | In a runtime example I can run it with tests and it would
               | behave fine if both values are same or first arg is
               | bigger, in Rust's case it would behave valid for ANY
               | combination of arguments.
        
               | dmm wrote:
               | Someone much more insightful than me pointed out that
               | most of the safety advantages of Rust are really a
               | cultural phenomenon, rather than a strictly technical
               | one. You could write unsafe unsafe Rust that derefs
               | invalid pointers all day but when building systems and
               | libraries with Rust, people value safety and Rust enables
               | that as a priority.
        
               | pjmlp wrote:
               | Yes, that is kind of true.
               | 
               | It is also what attracted me into C++ coming from Turbo
               | Pascal and Turbo Basic, back in the early 1990's.
               | 
               | Although C++ culture could be much better towards safety,
               | it is definitely better than whatever WG14 is doing, or C
               | has brought into the picture for the last 50 years.
               | 
               | Also anyone that just copy pastes C like code into C++,
               | is the kind of developer that will be using unsafe{} all
               | over the place, on the languages that have them.
        
               | imtringued wrote:
               | Telling people to stop using unsafe is much easier than
               | telling people to not have undefined behaviour.
               | 
               | C developers like telling themselves that only people
               | with bounded rationality make security critical mistakes.
               | All the skilled C developers have ascended beyond the
               | mortal realm and would never let themselves be chained up
               | with crutches for the weak like affine types or
               | overflow/bounds checking.
        
               | worik wrote:
               | > a cultural phenomenon, rather than a strictly technical
               | one. You could write unsafe unsafe Rust that derefs
               | invalid pointers all day
               | 
               | Yes you could. But I do not think " cultural" is the
               | right term for not putting all your code in an `unsafe`
               | block
        
             | intelVISA wrote:
             | The fatal mistake here is using the STL...
        
               | tialaramex wrote:
               | The safe thing Rust did here is affordable in Rust
               | (Option<T> is the same size as T for many T including all
               | references) so they could afford to do it, whereas it's
               | expensive in C++. Could it have been made cheaper in C++?
               | Sure, but safety wasn't their priority so who cares?
               | 
               | That prioritisation applies to the whole ISO language,
               | not only to the standard library.
        
               | pjmlp wrote:
               | Talking about C?
               | 
               | https://godbolt.org/z/vYcMhE9h7
        
               | tialaramex wrote:
               | You turned on a feature which helps diagnose this type of
               | mistake at runtime, and it helped you by diagnosing the
               | mistake at runtime. What does that prove?
               | 
               | What I'm talking about is that Rust's Option is very
               | cheap in all the cases where it can be very cheap, which
               | makes this whole design feature more affordable. C++
               | eventually grew std::optional which is not powerful
               | enough for this work and yet is also bigger and slower.
               | They _could_ have done better, but safety wasn 't a
               | priority.
        
               | pjmlp wrote:
               | The fatal mistake is not enabling bounds checking, which
               | most STL implementations support.
        
               | intelVISA wrote:
               | -Wabsolutely-everything
               | 
               | I haven't written the Language of Kings in a while: does
               | GCC -Wall truly enable all warnings yet?
        
               | staunton wrote:
               | Obviously not. It would be an instant scandal if they
               | tried that.
        
             | FpUser wrote:
             | And what should be defined behavior? There are quite a few
             | choices that depend on particular situations. Feature
             | designers have no knowledge about what would you want so
             | they left it up to application programmer to check the
             | situation upfront and act accordingly to their wishes. If
             | you want same behavior across the whole application you can
             | always write generic function doing just that.
        
               | Ygg2 wrote:
               | Return either value or an EMPTY placeholder. Look Java
               | did it. It returns a nullable value or Optional.
        
               | FpUser wrote:
               | Then I have to check the result anyways. Same thing
        
               | [deleted]
        
               | Ygg2 wrote:
               | No. In Rust check is there by default, with optional
               | unchecked access. In C++ the safety is off by default and
               | you have to remember to check.
               | 
               | It's like Yaml parsers, they had "load_yaml" which is
               | unsafe and "safe_load_yaml" which is the secure option.
               | Imagine no surprise when Rubyists went for shorter safe
               | looking method and got their servers pwned.
        
               | FpUser wrote:
               | I think you are wrong. I looked and in Rust doc it says
               | that: "Removes the last element from a vector and returns
               | it, or None if it is empty". You better be doing check
               | for "None".
        
               | Ygg2 wrote:
               | Problem is, you call pop on empty vector in C++, you get
               | nasal demons. Not a case in Rust.
               | 
               | I'm not a C++ expert but here is my understanding. Vector
               | is essentially a tuple of (dynamic_array_address: ptr,
               | size: size_t, capacity: size_t). To pop a value from
               | vector you just decrements size. So what happens when you
               | have empty vec? Your size is 0, and you're substracing 1,
               | which causes undefined behavior.
               | 
               | Correct way is to check BEFORE you pop_back().
               | 
               | In Rust, any number of invocation of pop() will not
               | result in undefined behavior. You can ignore the value,
               | or you can check it, but it doesn't expose you to UB just
               | for slightly misusing a vector.
        
               | LegionMammal978 wrote:
               | The check is still there, that's not under dispute. GP is
               | saying that it's still not the "same thing" as C++, since
               | Rust will refuse to compile the code unless you either
               | perform the check, or actively opt into unchecked access.
               | In contrast, C++ will happily compile the code if you
               | unintentionally neglect to perform the check.
        
               | FpUser wrote:
               | Are you saying that Rust will refuse to compile code that
               | does not explicitly check option return result for empty?
        
               | __jem wrote:
               | Yes, you cannot use the result without matching on the
               | result/option type.
        
               | Ygg2 wrote:
               | >Are you saying that Rust will refuse to compile code
               | that does not explicitly
               | 
               | Depends what you mean by explicit.
               | 
               | Look at it like this:                   pub fn main() {
               | let mut vec : Vec<i32> = vec![];           let x =
               | vec.pop();           println!("{:?}",x); // prints: None
               | }
               | 
               | In this case you see the value is missing.
               | vec.pop().expect("I want a value")
               | 
               | will panic with "I want a value" because value is empty.
               | And most rigorous way to deal with it is:
               | if let Some(x) = vec.pop() {
               | println!("{:?}",x);          } else {
               | println!("EMPTY"); // prints: Empty         }
               | 
               | will either print value or EMPTY depending on what the
               | vector contains.
               | 
               | Types like Result will issue warning that you didn't
               | handle the cases.
        
               | dureuill wrote:
               | To be precise, Rust will refuse to compile code that
               | accesses the value without checking that it is not empty.
               | 
               | Rust does that with the combination of "sum types" (aka
               | "enums with data") and pattern matching. An `Option<T>`
               | is either `Some(T)` or `None`. Matching on an option with
               | the pattern `Some(value)` creates a new syntactic scope
               | where the value is accessible. This scope is only entered
               | if the `Option` is actually non-empty.
               | 
               | All ways of getting the value from the option are
               | ultimately shorthand for a match in that way. For
               | example, option.unwrap() will either get the value if it
               | there, or panic. Option.unwrap_or(x) will either get the
               | value of it there, or use x instead.
               | 
               | In practice this is x100 less error prone than C++.
               | Source: was burned by vector.front() on empty vectors
               | more than once. In C++, UB. In rust, usually the "empty
               | case" is considered when first writing the code, or at
               | least caught during review (unwraps tend to be very
               | visible in reviews)
        
               | mughinn wrote:
               | You should always check
               | 
               | The argument is that Rust forces you and in C++ you can
               | forget/the compiler can do what it wants
        
       | sheepscreek wrote:
       | I'll get straight to the point I want to make: Rust is suffering
       | from an identity crisis. Much like Javascript.
       | 
       | Realizing this, I thoroughly feel the need for a "Rust, the good
       | parts" doctrine.
       | 
       | A good portion of use-cases could be successfully implemented
       | with a small subset of language. The small subset doesn't need to
       | be any more complicated than Go. And in doing so, we'd be
       | reducing the entry barrier for masses and encouraging wider
       | adoption.
       | 
       | Edited: For clarity
        
       | rurban wrote:
       | Interesting. So it looks like once he let the C++ folks in, they
       | started to damage it. As they did with their own design decisions
       | before.
        
         | sapiogram wrote:
         | > So it looks like once he let the C++ folks in, they started
         | to damage it.
         | 
         | At least in terms of his personal preferences. I don't think
         | it's true in terms of market share, given the massive success
         | of Rust.
        
       | superkuh wrote:
       | Funny how every time I click a link to a dreamwidth.org hosted
       | blog the,
       | 
       | >"Hello, you've been (semi-randomly) selected to take a CAPTCHA
       | to validate your requests. Please complete it below and hit the
       | button!"
       | 
       | ...pops up and the button doesn't actually work. Truly one of the
       | worst blog hosts out there if you actually want everyone to be
       | able to read what you write.
        
       | parasense wrote:
       | He's already created several programming languages, and there is
       | still time to create his ideal.
        
       | cryptonector wrote:
       | > Environment capture. I often say (provocatively) that "I hate
       | lambda", but lambda is actually (at least) two separate language
       | features: one is a notation for anonymous function literals;
       | another is equipping those anonymous function literals with
       | environment capture. I don't really care either way about having
       | such a literal syntax, but I really do dislike equipping it with
       | environment capture and think it's a mistake, especially in a
       | systems language with observable mutation. Early Rust had
       | something called "bind expressions" which I copied from Sather
       | and which I think are better (clunkier but better). Rust gained
       | lambda-with-environment-capture in a single package which I
       | didn't object to strongly enough, as part of trying to make
       | interior iteration work on LLVM-with-no-coroutines, and this
       | motivation was later removed when we moved to exterior iteration.
       | But "if I were BDFL" I would probably roll back the environment
       | capture part (it's easier to tolerate for non-escaping closures
       | which many are, eg. in non-escaping coroutines / stack iterator
       | bodies, but .. eh .. complex topic).
       | 
       | TFA loses me here. I rather like the Fn/FnMut/FnOnce business,
       | though yeah, closures of dynamic extent are very limited unless
       | you Box them to make them of indefinite extent... and so the
       | whole language lacks that character that functional languages
       | with GCs have, but it's still functional, just functional with a
       | straight-jacket.
       | 
       | Earlier in TFA there's a mention of exterior iteration as in
       | generators, and I want to point out that while generators are
       | very nice, they are not a substitute for closures. Icon, for
       | example, had iterators and first-class co-routines ("co-
       | expressions"), but no closures, and so where one needed closures
       | one had to use co-routines (costly!). It's true that with
       | generators one needs closures less than without generators, but
       | still, closures are very important.
       | 
       | > Traits. I generally don't like traits / typeclasses. To my eyes
       | they're too type-directed, too global, too much like programming
       | and debugging a distributed set of invisible inference rules, and
       | produce too much coupling between libraries in terms of what is
       | or isn't allowed to maintain coherence. I wanted (and got part
       | way into building) a first class module system in the ML
       | tradition. Many team members objected because these systems are
       | more verbose, often painfully so, and I lost the argument. But I
       | still don't like the result, and I would probably have backed the
       | experiment out "if I'd been BDFL".
       | 
       | This loses me too.
       | 
       | It's great then that Rust didn't have a BDFL! :)
       | 
       | > Underpowered existentials. The dyn Trait mechanism in Rust
       | allows runtime and heterogeneous polymorphism, a.k.a.
       | Existentials. These types are useful for many reasons: both
       | solving heterogeneous representation cases and also selectively
       | backing-off from monomorphization or inlining (when you have
       | those) in order to favour code size / compile time or allow
       | dynamic linking or runtime extension. Early Rust tried to use
       | these extensively (see also "first-class modules") and actually
       | had an intermediate-rigidity type called an obj that was always a
       | sort of Cecil-like runtime-extensible existential glued to a
       | self-type record that allowed method-by-method overriding at
       | runtime (almost like a prototype-OO system). Today's Rust
       | strongly discourages the use of any such dynamic dispatch, a
       | feedback loop arising from both technical limitations placed on
       | them and a library ecosystem that's taken that as a sign never to
       | use them.
       | 
       | This, on the other hand, is a brilliant observation and I agree
       | with it as with much else in TFA.
       | 
       | > Tail calls. I actually wanted them! I think they're great. And
       | I got argued into not having them because the project in general
       | got argued into the position of "compete to win with C++ on
       | performance" and so I wound up writing a sad post rejecting them
       | which is one of the saddest things ever written on the subject.
       | It remains true with Rust's priorities today, I doubt it'll ever
       | be possible across crates (maybe maybe within), but as with stack
       | iterators IMO they're a great primitive to have in a language, in
       | this case for writing simple and composable state machines, and
       | "if I were BDFL" I probably would have pointed the language in a
       | direction that kept them. Early Rust had them, LLVM mostly made
       | us drop them, and C++ performance obsession kept them consigned
       | to WONTFIX bug status.
       | 
       | Oh dear. TCO is essential, IMO. I understand that it may not be
       | possible in cross-crate cases, but still, TCO is very important.
        
       | PhilipRoman wrote:
       | Haven't followed Rust too much, but I'm always surprised when I
       | hear that Rust is too difficult or not ergonomic. As I understand
       | it is meant to be a systems level language; something you'd use
       | to write kernels, TCP stacks, browsers and ssh daemons.
       | 
       | Anyone writing these things today in C or C++ already understands
       | object lifetimes and Rust just adds a static checker for them.
       | 
       | In such projects churning out lines of code is not the
       | bottleneck, ease of development should not be prioritized over
       | long term maintainability.
       | 
       | Why on earth would you try to rewrite python CRUD apps in Rust?
        
         | LinXitoW wrote:
         | > Why on earth would you try to rewrite python CRUD apps in
         | Rust?
         | 
         | Because Rust has a lot of incredibly helpful features that make
         | bigger systems far less of a pain to maintain. I work in
         | Java/Kotlin, and my entirely gut-based estimate is that 66% of
         | problems wouldn't happen in Rust.
         | 
         | My big favorites are: - Sane, well-defined, enforced, opt-out
         | error handling - Sane, well-defined, enforced, opt-out handling
         | of "missing" values - Exhaustive switching - WITH usable ADTs
         | (enums) for encoding valid state
         | 
         | None of the web languages I know have all of that.
        
           | marcosdumay wrote:
           | So... Haskell, OCaml, F#, Elm?
        
           | kaba0 wrote:
           | Scala has all of that and predates Rust by far.
           | 
           | But even Java is pretty much there: checked exceptions are
           | available (and imo superior), but Optional is also an..
           | option. Kotlin/scala can handle nulls, but java can also
           | statically analyse every usage with annotations.
           | 
           | switch expressions are exhaustive in java over enums _and_
           | sealed ADTs. (Rust has a misnomer, their enums are ADTs, java
           | has both real enums and ADTs now).
        
             | pitaj wrote:
             | Rust enums cover both ADTs and enums.
        
         | moonchrome wrote:
         | >Anyone writing these things today in C or C++ already
         | understands object lifetimes and Rust just adds a static
         | checker for them.
         | 
         | As someone who only used Rust casually - understanding object
         | lifetimes and knowing how to encode this in Rust type system is
         | not the same thing. Not to mention that Rust can't statically
         | prove some things that are valid (eg. cyclic references).
        
         | twic wrote:
         | > Anyone writing these things today in C or C++ already
         | understands object lifetimes and Rust just adds a static
         | checker for them.
         | 
         | I've certainly seen seasoned C++ programmers saying this. Of
         | course, a few of them of say they understand object lifetimes
         | so well, they they don't need the static checker!
         | 
         | > Why on earth would you try to rewrite python CRUD apps in
         | Rust?
         | 
         | This is one of the great mysteries of our times. I do think the
         | enthusiasm for using Rust for web apps and such (a) is
         | misplaced and (b) has been a drag on Rust developing into a
         | better C++ replacement.
        
           | bluGill wrote:
           | > Of course, a few of them of say they understand object
           | lifetimes so well, they they don't need the static checker!
           | 
           | There is a difference between understanding lifetimes and
           | being able to keep track of them. I have a lot of objects in
           | my more than 10 million lines of code. Most of them have
           | simple lifetimes that are easy to track, but a few for
           | reasons (which may or may not be valid - often the reasons
           | are it was built in C++98 and updating to modern lifetimes is
           | hard when it is used all over) have complex lifetimes that
           | are tedious to track. It isn't that I can't, it is that I get
           | bored/make mistakes and the static analyzer wouldn't (Or
           | course C++ can't be statically analyzed, but if it could the
           | static analyzer wouldn't fail for the same reasons I fail)
        
             | xiphias2 wrote:
             | I think there's enough data that even the best human
             | programmers and lifetime checkers aren't able to track
             | lifetimes over a 10 million line code base.
             | 
             | That's why we're stuck with lifetimes as function
             | contracts.
        
             | iMerNibor wrote:
             | Being able to stop constantly keeping things in the back of
             | your head and just trusting the compiler to complain if
             | something is off was the biggest differentiator for me by
             | far. Less footguns = more better
        
               | bluGill wrote:
               | When I switch to C++11 and unitue_ptr things got a lot
               | better. There is still a lot of cruft from old code, but
               | C++ is a lot better as of 12 years ago. I don't let
               | people manage raw pointers without good reason (I
               | wouldn't let someone use unsafe rust without good reason
               | either)
        
         | lukebitts wrote:
         | I personally really like the type-system, for any kind of
         | project really
        
       | jimwhite42 wrote:
       | On the "Async/await" wrt IO and FFI, I wonder what Graydon and
       | others think about the Erlang BEAM or the GHC runtime
       | implementation.
        
       | olalonde wrote:
       | > Explicit lifetimes
       | 
       | I never understood that one either. There is always only one
       | "solution" that will make your program compile, so why not just
       | let the compiler figure it out?
        
         | steveklabnik wrote:
         | I wrote a blog post about this.
         | https://steveklabnik.com/writing/rusts-golden-rule
         | 
         | I did not mention lifetimes, but the principle is the exact
         | same: the lifetime annotations are an API promise, and so
         | inferring them means that a change in the body of the function
         | would change the promise, breaking other code.
        
       | worik wrote:
       | > basically every language discovers the long way that financial
       | math is special and, at great length, eventually adds a decimal
       | type.
       | 
       | And every case it is wrong
       | 
       | Financial math is not hard when you know how
       | 
       | Use integer types not horrific cludges like "decimal"
       | 
       | Just because IBM did it does not make it right. It is wrong
        
       | smasher164 wrote:
       | I admire Graydon's humility on the subject--and sure enough, I
       | disagree with some of his ideas in this post, and agree with
       | other ones.
       | 
       | - Explicit lifetimes are what make rust what it is. It would have
       | surely failed had they not been introduced.
       | 
       | - I disagree about having a first-class module system instead of
       | traits. Coherence and implicit instance resolution are a core
       | value of Rust. `Send / Sync` are key examples of that.
       | 
       | - Green threads probably wouldn't been viable for rust, given the
       | kind of programs it's targeting.
       | 
       | - Pretty much everything else however, I agree with.
        
       ___________________________________________________________________
       (page generated 2023-06-05 23:04 UTC)