Rust in 2023: Growing Up
smallcultfollowing.com
smallcultfollowing.com
In those cases you don't want to bring out the big guns with dyn traits. But the current approach involving huge match forests, macros, helper functions and code duplication plainly sucks ^^'.
This could be solved by making enum variants proper types. Something that has been proposed already, but which has been rejected due to lacking capacity.
But yeah, typed enums would be cool. I imagine as more const work is done typed enums will eventually be a natural next step.
- Create all the variants as normal structs
- Create enums around different subsets of them that just wrap each struct in a variant of the same name (including from/tryfrom impls)
- Have methods for "casting" one of these enums into another
The second and third points can be made fairly non-painful with a macro where you just say "enum X can be structs Y, Z, A, B..." and it creates the variants and impls all the traits for you
But yeah. This is one of the bigger pain-points to implementing certain stuff in Rust
Edit: just realized you were talking about something slightly different from me, but I think you could extend this pattern to get it:
Have a trait that all variants implement, implement it on the enum too, and have the enum delegate to the variant structs (since those are already separate). The wiring should be automatable with the same macro
Compare the following
#[repr(u8)]
enum Dense {
A {
foo: u8,
bar: u16,
}
}
struct AStruct {
foo: u8,
bar: u16,
}
#[repr(u8)]
enum Padded {
A {
inner: AStruct
}
}
The Dense enum will be 4byte, because Rust will use the u8 tag as an implicit first field of a tagged union with all the variants fields.However Padded will be 6 bytes, because AStruct is padded to 4byte, as the u16 causes it to have 2 byte alignment. That two byte alignment causes it then to be padded again when combined with the u8 tag.
You can't mark AStruct as #[repr(packed)] either, because then the compiler can't proof that the struct will never be moved out of it's alignment providing variant (and unaligned field access is a no no for rust).
The only other thing I could think of would be to transmute between the structs representing the variants, but I think that will probably cause UB because it destroys an ungodly amount of providence information.
In general, coming from TypeScript I'd like to see a lot more "duck" types in Rust that describe shapes the compiler already knows how to deal with, just placing additional constraints on what the user can do with them. Individual enum variants, but also subsets of a given enum (where the representation can still just be the same as the full enum)
Along similar lines, I'd really really like "anonymous structs" (basically tuples with named members). That seems especially low-hanging since tuples already exist; it's almost just sugar
I think there's a lot you could do here that would fit really neatly into Rust's existing type system, but like in the RFC above I assume it's just a matter of devs not having time to implement it all
Now... a neat compromise could be something like this
fn foo<T: { bar: i32 }>(my_struct: T) {
}
foo({ bar: 12, stuff: "abc" })
That would fit a lot more smoothly into Rust's type system than the automatic subset-matching would, but even then, I'd be content without it. The most basic version of anonymous structs is hardly more than sugar over tuples, it just seems like such an easy winWho doesn't prefer to just build something in a language where you know first time setup is pretty straight forward as opposed to wasting hours setting up some convoluted web project by hand?
I think Rust needs to invest in providing a rich standard library, even if it is just its own library that is fully optional, as long as the core team works on and maintains it, I think it could do plenty of net-good for Rust as a whole.
I'm also thinking of my favorite things about languages like Racket and Python which is a really simple editor that is built-in. Again, Rust needs to be able to build UIs to achieve this.
I am not sure why people get so hung up on the stdlib's stability guarantees. We could have a second unstable "nonstandard" library for example, and eliminate this "issue" completely.
Even a crate "distro" like the other user said would be nice. Something to allow us to establish some level of trust over what we are using. Right now we have people mindlessly adding 100s of crates with no concern about the supply chain.
Heck you can see it in this very thread with a complaint about too many new things even though they’re compatible!
I am thinking of nom for example, lots of people are already using nom and are comfortable with it's current level of stability, so if it was part of a crate distro people would just determine if nom was stable enough for them and use it, otherwise find something else.
I do appreciate that the real standard library and core language are stable though just to be clear. I also appreciate that the stdlib tries to stay somewhat lean rather than shoving the whole world in it.
I am just a random hobby rust user so it's very possible I am talking a bunch of nonsense!
Also thanks for replying, it's always cool to see you active in these threads :)
Is first time setup of Rust projects a regular issue for you? That's not my personal experience, but I am of course biased. 99% of projects are "git clone && cd && cargo build," done. 0.95% are "oh, also install some C library via the package manager," which is a one-time thing per library.
In practice, some folks have tried to get a "richer standard library" going, and they've never seen community adoption, and so end up abandoned. It's been quite a while since this sort of thing was proposed by the project, but back when it was, the community also pretty resoundingly rejected the idea.
It did take about a minute to figure it out, and then I also spent another minute fiddling with the examples because I wanted to say funnier stuff.
Built, ran, checked on my local machine, very convinced, Rust makes a pretty good web server, would recommend.
I feel like that wasn't your point ?
〉time
The current time is: 12:38:36.95
〉cargo new foo
〉cd foo
〉cargo add axum
〉code . (and then copy-paste the axum docs hello world from docs.rs/axum into src/main.rs)
〉cargo build
〉time
The current time is: 12:43:37.44
Most of that is waiting on my crates.io index to update (which isn’t something that happens every time you start a project), and that, hilariously, the axum hello world also requires that you do a `cargo add tokio --features macros rt-multi-thread`, which they do not document, and I had to figure out myself. Most packages don't have that issue, and I'm filing a bug right now.> I can have a Go net/http project up quicker than all the above just cause of muscle memory
I suspect that this has a larger impact on your experience than you're giving it credit for. You shouldn't discount familiarity, it's a huge bonus! And a good reason for you to do this stuff in Go and not Rust. But it doesn't mean it's universal.
Am I going to argue that Rails is not wildly productive? Absolutely not. But at least, for me, the package existing in the stdlib isn't the different factor here. Rails and Django both aren't in the Ruby and Python standard libraries either, you know?
I'm really into things like Django and ASP .NET these days, and last I looked (months ago?) Rust doesn't offer anything fully batteries included yet (relative to those two frameworks), heck even Phoenix is really good in this regard.
I like axum because it’s maintained by some prominent Tokio folks and fits my sensibilities. There’s lots of good choices out there.
You’ll notice the sibling comment did the same thing but with a different web framework they prefer. That’s great too. But other than the choice, it’s entirely agnostic. Another sibling even posted a step by step tutorial, if that level of specificity is helpful.
When people say "we need a rich standard lib" they are more interested in a "blessed set of crates" than a "it should be pre-downloaded with the compiler".
While two different ones were mentioned in this thread even, a new user would be easily served by either.
cd ./example-server
cargo add actix-web
(quickly searches crates.io for `actix-web`)
Cool, copy and paste:
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
format!("Hello {name}!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(greet)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
takes ~a minute. Not sure std would have saved me any time, I would have just searched the stdlib docs instead of crates.io for actix. And I guess I wouldn't need to `cargo add actix-web` lol that was not the bottleneck.The benefits are I'm sure not new to you. Updates and releases can be completely independent of Rust itself, and by not being in the standard library maintainers are able to deprecate and remove misfeatures when needed as opposed to having to support something that holds back a library for forever.
The one downside though is that there is the unfortunate reality that—by leaving important parts of the ecosystem to random crate authors—important and heavily-used crates which would be part of other langs' stdlib can become unmaintained over time. This hasn't been too large a problem so far, but I do wonder if more could be done to promote community adoption of crates that are perceived to be more core to the ecosystem. The nursery is probably a good approximation to what I'm looking for, but without necessarily coming along with the implication that it should ever end up in `std`.
And I suspect standard libraries get far less 'maintenance' than many people think they do, especially compared to a popular external library.
Both could publish the exact same HTTP server crate but with one of those two, I can a) have some confidence that it's a probably a reasonable default choice, and b) assume that it's much more likely to be stable and mature than simply abandoned.
There are many UI libraries for Rust; I’ve tried most of them, and all of them are radically different in their approaches. None of them are anywhere near as good as most UI frameworks in other languages (in my opinion).
Bringing a UI framework into the stdlib at this point in time would be terrible because the community still hasn’t figured out the best way to write a UI framework and it would stifle innovation in this developing section of the language.
It is way too early to standardize any such thing now. There are, give or take, 5 promising UI toolkits, and a dozen others that are best thought of as experimental and may contain interesting ideas. There's a very wide diversity in approaches, and much of that diversity is not accidental but can be traced to different requirements. If you need to deploy an app both on the web and natively, then that pushes you toward adopting at least some web technologies. If you're working on games or game tooling, then the tradeoffs of immediate mode GUI are much more appealing. Another major axis is how much to rely on platform capabilities for stuff like text layout vs rolling your own.
We need to do a lot more exploration of the design space. I believe there is potential to build really compelling UI in Rust. It's entirely possible we end up with fairly different approaches for different use cases, hopefully converging on common infrastructure for things like accessibility.
But if we standardize anything we have now, it risks "the standard library is where modules go to die."
A moribund API provides me more value than one that doesn't exist at all.
That usual meme against rich standard libraries keeps forgetting that tiny and very important property of standard libraries.
Also with Cargo, what's the difference between a 3rd party library and the stdlib? (none really)
Also: when the package ecosystem is as strong as Rust's, it's less important to supply first-party things just because they're useful, and more important to supply things that establish a common interface that multiple third-party packages can then agree on as a standard. See things like the Future trait
I would also appreciate it if the standard library had a standard for UIs so you can build something UI library agnostic, that would be really interesting to see.
Until we have languages like Rust and Go make headway with GUI stacks, we will be forever cursed to live in a world full of Electron web apps.
Nowadays it only applies to Visual C++, game consoles and embedded platforms, because everywhere else it lost the leading role for full stack development.
I've heard this before but I've never really understood it.
Why on earth should language designers spend time and effort building a toy text editor when damn near everyone has VS Code, JetBrains, Sublime, some flavor of vim or emacs, etc. available to them? Why would anyone want to learn a new, completely bespoke editor just to play around with a new language when you can install a language extension for the editor you're already using in seconds?
I'm genuinely asking here. What am I missing?
I see the complaints about things like stdlib and I don’t get it - the core team is doing an excellent job of keeping rust doing what it should do best, and the crate system and community fills that gap. If you want a big stdlib then use Python.
It is great that there is an active Rust community with tons of useful crates, but it can be daunting as a newcomer or when introducing Rust into an organization to discover and adopt the "right" crate(s) for common tasks that the standard library has no answer for.
An example common task is asynchronous programming. For my current project, I am better off using thread::spawn, async / await, async-std, tokio, smol-rs, or something else? I had tentatively selected async-std, but then I came across this thread: https://github.com/async-rs/async-std/issues/992#issuecommen...
If I select a given crate today for a given common task, will the crate still be around and maintained in three years? Will I need to rewrite my project to use the latest crate du jour?
I wonder if it would be possible for the Rust community to design / define a common library of traits for common tasks that could be used as "interfaces" (sorry, Java background) that library crates could be implemented against.
This would provide better stability for crate users and help to avoid analysis paralysis and uncertainty when selecting crates for common tasks that the standard library has no answers for.
There could even be multiple libraries of traits grouped by the type of task. This would allow for more natural groupings based on usage (instead of one huge library of traits) and permit independent changes from other defined libraries of traits.
The Ada programming language has a similar idea called "Standard Library Annexes". See the bottom half of http://www.ada-auth.org/standards/22rm/html/RM-TOC.html where the sections start with alphabet letters instead of numbers. There are standard annexes for: "B. Interface to Other Languages", "C. Systems Programming", "D. Real-Time Systems", "E. Distributed Systems", "F. Information Systems", "G. Numerics", and "H. High-Integrity Systems". Ada toolchains do not have to implement all of these annexes to be a standards-compliant Ada toolchain, but it gives those that do a standard interface so that they are more interchangeable, and code written against them does not have to be rewritten.
Rust is wonderful and I want to see it improve.
Is an idea like this feasible? If not, why not?
In 1983 if I design a Video interface I guess we need PAL vs NTSC to handle colour standards, and also we need Interlacing, that's a thing. Should we have a fixed list of resolutions? Nah, let's splash out, user defined, that's future proof.
Oops, here we are in 2023 and while nobody cares about Interlacing, and the arbitrary user defined resolutions allow me to express 4K display, there's nowhere to pick 120fps, or to specify 12-bit colour.
True. However, without more standardized components the Rust ecosystem could end up like the Common Lisp library problem: lots of libraries in various states of maturity and maintenance with no obvious choice of what to use to solve your problem.
Big standard libraries help to drive corporate and enterprise adoption. It is part of the reason why Java and Python are so widely used in corporate / enterprise environments. It brings a lot of standardized functionality and longer-term support that those kinds of environments want.
We rely on the Rust standard library because it will be maintained for a very long time and has mostly stable APIs. Without a bigger standard library or some other kind of standardization, we would build our own libraries that might re-invent the wheel, but at least we know that our libraries will be maintained.
Originally all the UNIX libc that didn't land in ANSI/ISO C89 and became POSIX instead.
Rust is making inroads in the Linux kernel which is good.
On HN I have seen links to many interesting blogs and articles about how painful interfacing Rust with the eccentricities of C / libc can be.
Unless there is a better replacement for POSIX (which as written requires C), then POSIX will remain along with all of its eccentricities.
In Rust's case maybe what is required is a kind of curated set of modules, a bit like Java EE defined a set of basic functionality (in multiple JSR specifications) that every application server should provide, including Spring based ones.
Frankly I don't see what "annexes" would buy the Rust ecosystem. You don't pay for what you don't use to begin with, and there's zero advantage to multiple implementations of the standard library. That's an antifeature in my opinion.
> If I select a given crate today for a given common task, will the crate still be around and maintained in three years? Will I need to rewrite my project to use the latest crate du jour?
The same is true in any ecosystem. How likely is your project to exist in three years, and how much would it cost to replace the dependency if it gets deprecated? It's just not that high of a risk to worry about.
I am not talking about multiple implementations of the standard library, but rather, optional groupings of traits for common tasks. For example, could the community design / define traits for an asynchronous programming runtime?
>> The same is true in any ecosystem. How likely is your project to exist in three years, and how much would it cost to replace the dependency if it gets deprecated? It's just not that high of a risk to worry about.
The company I work for supports products that have lifetimes measured in decades. I would love for us to use Rust, but the management will not approve Rust because of how unstable the ecosystem is. Updating products because the libraries we rely on are no longer maintained would cost time and money that could be spent in ways that would add value that our customers could appreciate.
And I'm also looking forward to that keyword generics initiative, hopefully that team can look at how OCaml implemented their algebraic effects system, not sure if it's totally comparable however.
In service of what though? I love Haskell and HKTs, but Rust is a different language and I don't find myself actually reaching for HKTs, even when writing libraries.
Just my personal opinion, I think adding these features would be to Rust's detriment. I am happy to do my exploration into more complex type level features in another language in order to not add everything and the kitchen sink to Rust.
https://github.com/rust-lang/rust/pull/96709#issuecomment-11...