Rust 1.82 on wasm32-unknown-unknown has multivalue and reference-types extensions enabled by default. This created a myriad of issues with non-compliant browsers, mostly Safari, which based on my experience has broken implementation of these at least up to version 16.3 on iOS, which is only 1.5 years old.
I saw that Rust 1.84 plans to land wasm32v1-none that disables such extensions, which is great. However, according it its documentation it is also a nostd target.
However, my use case which I think is also the use case of many people using Rust and Wasm in production is to have a Webassembly MVP-only target so that two versions of the .wasm can be built and the compatibility one can be served to old Safaris.
So, my ask is, can we have such a target? Something like wasm32v1-unknown-unknown.
Yes, but it requires rebuilding std. I also do not know how well rebuilding std combines with wasm-pack and Webpack's WasmPackPlugin. The thing is, to use Rust on the web, most people want std. But also many people have to support Safari despite its bugs. So I think that it would be helpful for this common use case to be supported out of the box.
Have you tried? If it does not work for some reason, then you should to ask wasm-pack people to add support for this case. Hacking a new target seems like a wrong tool for this.
Small rant: I believe that having an "std" for wasm32-unknown-unknown was a big mistake. People should properly write crates to use alloc instead of relying on "std" which panics or returns errors on each non-trivial sneeze. Yes, there is an issue with HashMap, but it should've been resolved properly by exposing an overwritable "system" randomness source in a sysroot crate.
Indeed I didn't. It feels that rebuilding std in the CI is not a great idea, compared to using an old compiler for now. But maybe with some caching it is feasible.
Small rant: I believe that having an "std" for wasm32-unknown-unknown was a big mistake.
I can't argue on that as I do not have enough experience of Rust's internals. But I'll like to point out that as a user, when choosing a product, I'll like it to work out of the box with its basic features. And having a proper std (as Rust has now) feels like basic features nowadays. So for what it is worth I'm quite happy that I have std on the web. Maybe it should not have been in wasm32-unknown-unknown though, maybe in some wasm32-web or something like this.
And that's what I'm arguing for, something that works straight out of the box for the common use cases on the web, so that Rust is a suitable solution for web development in a real industrial context.
Std provides OS abstractions. Bare wasm does not offer IO, threads, processes, randomness, allocations, time etc. etc. so it is correct to treat this as an embedded no-std target.
To get std on wasm-in-a-browser you'd need standardized host bindings. I think wasi targets are that? (uncertain, haven't looked at wasi in depth) If yes then you need a -wasi target, not a -none.
If you're missing some crate that should work without std but doesn't then this is not a rust issue but an issue with that crate. Embedded developers have to deal with that too. File a bug with the crate in question.
Std provides OS abstractions. Bare wasm does not offer IO, threads, processes, randomness, allocations, time etc. etc. so it is correct to treat this as an embedded no-std target.
I see, on this I agree. I didn't realize the collection library was (almost) all in alloc. Thank you for the clarification. Indeed if HashMap would be in alloc, then the bare target would be good enough for me. So, hopefully that happens soon.
You can use hashbrown on no-std targets, which is the implementation under std anyway. You still have to decide what to do about hasher randomness yourself though.
What could potentially work is a "wasm32v1-web-bindgen" which uses the wasm_binden protocol to bind to Math.random(), Date.now() (js), and Performance.now() (web). That covers randomness and time. IO and commands can just unconditionally return ErrorKind::Unsupported and be "in spec," but thread spawning just needs to panic[1].
I think that with the single thread assumption that allows the sync types to just be the cell equivalents, that does cover all of the requirements of std. The downside being that it no longer "just works," requiring the user to provide the wasm_bindgen imports to the wasm module, likely even if they don't directly use the functionality. Of course this won't be an issue if you're already going through wasm-pack.
It's probably better to just panic, but a different model would be to "spawn" the thread in an already panicked state, so thread::spawn "succeeds" but the JoinHandle is immediately attached to a "panicked thread" and no user code is ever run on that "different thread." âŠī¸
What could potentially work is a "wasm32v1-web-bindgen" which uses the wasm_binden protocol to bind to Math.random(), Date.now() (js), and Performance.now() (web).
Yes that would be awesome! I am using wasm_bindgen and I guess most Rust-in-the-browser users do so. Maybe also integrate web-time so that std::time works properly (including in web workers)?
Over time, maybe more API could be supported, such as the File System API, once they stabilize and become widely available?
If such a target would be added, I suggest to add a matching wasm32-web-bindgen that has similar instruction support requirements as wasm32-unknown-unknown but provides the same nice bindings as wasm32v1-web-bindgen. I believe such a target would significantly improve the ergonomic of Rust on the browser.
Note: I have updated the thread title to match the outcome of the current discussion.
In case people wonder, I am asking these questions in relation to our Candli web-based educational game creation platform, which uses Rust alongside Typescript for performance-sensitive parts of the client.
The reason why we haven't done this from the start for wasm32-unknown-unknown is that wasm-bindgen's protocol is unstable. The wasm-bindgen cli checks that the metadata was created by the exact same wasm-bindgen version (including patch version). This is also why we have been able to fix wasm-bindgen to not depend on wasm32-unknown-unknown's broken C ABI. Any usage of wasm-bindgen in the standard library would require -Zbuild-std + some way to update wasm-bindgen for the standard library to match the wasm-bindgen cli.
Do you see a way forward? Like continuing with the current approach and having a wasm32v1-unknown-unknown that ships with std, or something like stabilizing wasm-bindgen's protocol to get wasm32-web-bindgen/wasm32v1-web-bindgen targets?
I'm agnostic to the technical solution, but I do care about Rust's story on the web, both because our startup already invested significantly into it (and Rust in general) and also because I believe that WebAssembly is a good way forward for the web (regarding efficiency, energy consumption, etc.). And hopefully things can become a bit better in term of platform support, as currently it is somewhat of a bumpy road, despite WebAssembly being on the homepage of the language.
I did a search, people have tried and failed, but the solution is probably to run wasm-pack with nightly. However, when used with a bundler such as webpack, wasm-pack is called by a plugin, such as wasm-pack-plugin. Configuring that plugin to use nightly is tedious. It might be doable with a rustup override or a rust-toolchain.toml, I didn't try myself.
The point is, having to do all these steps correctly is hard, error-prone, fragile and clearly not the user-friendliness expected of a first-class feature.
Doing some research on that question shows that it's not that trivial in practice. Take for example the image crate. Work on no_std has been going on for years. There is slow progress, and it seems that the current blocker is the lack of Read and Write traits in no_std (itself an issue opened for years). Of course there are (incomplete and not fully up to date) work arounds for that. Yet this shows that making a third-party crate no_std is not trivial, because Rust's std is not (at the moment) perfectly splitable between "things that depend on io" + "things that depend on dynamic memory allocation" + "rest".
I didn't say it would be trivial. But just because there are issues doesn't mean we should offer a non-functional std, otherwise we'd have to do that for all targets and then explain to users why the API surface is there but doesn't work.
If we only take the easiest immediate solutions then we end up with a pile of hacks. Which means until someone puts in the work to add a proper solution the hacks and workarounds will have to live downstream.
From a technical standpoint I agree with you. But wasm32-unknown-unknown does exist, is generously advertised on the language homepage and does provide std, although partially non-functional.
So while I do hear the arguments that it was a bad idea in the first place, because it exists and is widely advertised (which I assume was a strategic decision to widen the language adoption), people build things on top of it. So when suddenly the compiler updates and Rust builds stop working on a less than 2 years old Safari, it is really a problem for the users, who have invested in this technology on the assumption that it was seriously supported.
Of course one can easily argue that's the browser's fault, but at the same time one could counter-argue that the Rust product is now broken, and that an easy-to-use fix for the users already invested in the language should be provided. Finding such an accessible fix was my core thinking for suggesting wasm32v1-unknown-unknown in the first place.
That's not a good argument to proliferate a mistake. Proliferating would make the problem worse in the long run because it takes away incentive to do things properly, so even more people would start relying on std instead of alloc/core where they should.
To support old targets you can use an old rust version. This should buy you time to figure out how to migrate.
At least adding a -v1-unknown-unknown target with std wouldn't be very forward-facing. Working on -bindgen targets would seem more productive.
But I don't know if the wasm platform maintainers have already considered older browsers.
And you didn't mention the fraction of the usershare, which is typically something that gets considered when talking about browser features.
That's not a good argument to proliferate a mistake. Proliferating would make the problem worse in the long run because it takes away incentive to do things properly, so even more people would start relying on std instead of alloc/core where they should.
I hear and understand your point but it feels a bit user-unfriendly to me. For example, stating that the right thing to do is that people should rely on alloc/core obfuscates the reality that, as illustrated with my image crate example (that I found while trying to get rid of std in the smallest of my wasm, devoted to computer vision), the pieces necessary to do that smoothly are not yet all in place (e.g. the Read/Write traits not being in core).
To support old targets you can use an old rust version. This should buy you time to figure out how to migrate.
This is what we are doing at the moment.
At least adding a -v1-unknown-unknown target with std wouldn't be very forward-facing. Working on -bindgen targets would seem more productive. But I don't know if the wasm platform maintainers have already considered older browsers.
This I agree, having a proper std support on the web through a -bindgen target would be lovely.
And you didn't mention the fraction of the usershare, which is typically something that gets considered when talking about browser features.
According to stat counter, Safari has a 15-20 percent market share. And according to stats on a ruffle PR related to same issue, in November 2024 there were 17% of Safaris <= 16.3. So that is a 3% overall. In specific applications (e.g. tablets) it might be more.
In any case, if you have to use nightly to do this you might as well stick to Rust 1.81 for an easy life: either way you're crossing the line to using an unsupported version of the compiler.