Add support for `#[repr(transparent)]` on exported structs
This PR adds support for #[repr(transparent)] on exported types:
#[repr(transparent)]
#[wasm_bindgen]
pub struct TransparentU32(u32);
#[wasm_bindgen]
pub fn make_transparent_u32(value: u32) -> TransparentU32 {
TransparentU32(value)
}
#[wasm_bindgen]
pub fn get_transparent_u32(value: TransparentU32) -> u32 {
value.0
}
const r = wasm.make_transparent_u32(42);
assert.strictEqual(r, 42);
assert.strictEqual(wasm.get_transparent_u32(r), 42);
assert.strictEqual(r + 1, 43);
assert.strictEqual(wasm.get_transparent_u32(r) + 1, 43);
assert.strictEqual(typeof r, "number");
See #2156 and #1474 for the motivation behind this.
- [x] Transparent primitives (
u8,u16,u32,f32...) - [x] Transitive transparency (
Transparent(Transparent(a))==a) - [x] Zero-sized field(s) after the only non-zero-sized field (
Transparent(u32, ())) - [ ] Zero-sized field(s) before the only non-zero-sized field (
Transparent((), u32)) - [ ] Transparent
Option<T>,Box<T>,Result<T, E>... - [ ] Transparent classes, enums...
- [ ] Typescript type definitions
Fixes #1474.
By using this implementation it forces structs like
charto consider everything valid because we can't control what JS gives Rust.
How does that usually work across the FFI? Is it a non-issue because the FFI is unsafe anyway?
Yeah I would imagine that most other FFI is already "unsafe enough" that this concern doesn't come up. That being said I also don't personally see this much with FFI since the goal isn't to make it as nice as possible but rather get something working first.
One of the original motivations behind supporting repr(transparent) was just to provide a safer API on the JS/TS side (instead of manipulating the wrapped type directly). This could be achieved with some Typescript magic.
Another major motivation (much more important, imo) is to allow some types to be represented as JavaScript primitives to allow some operations such as === equality checks or comparisons to be performed more naturally. It also allows values to be serialised (sometimes necessary, e.g. when using the Drag and Drop API).
If the invariants introduced by some repr(transparent) types are a pain point, maybe a similar bindgen-specific attribute could be used to provide similar functionality? Another solution to explore would be to allow the user to define their own validation/conversion functions between the wrapped and the new type, but I'm not sure if this is desirable.
To me that only seems fair if From was already required when targeting platforms other than the Web
I don't know if forcing the user to write anything more than #[repr(transparent)] is a good idea. If wasm-bindgen needs more than that, I think it's better to have it as a separate bindgen attribute like #[wasm_bindgen(transparent)] or #[wasm_bindgen(transparent = Foo)]. This solution would solve the "which is the non-zero sized field?" problem by emulating type transparency at the bindgen level instead (with From/Into conversions at the FFI boundary). True transparency is still possible by adding #[repr(transparent)], but wasm-bindgen wouldn't care about it (just like it does today)
This PR is quite old but it adds something valuable to wasm-bindgen that I would find quite useful.