Wasm-bindgen: Automatically camelify names

Created on 16 Oct 2019  路  24Comments  路  Source: rustwasm/wasm-bindgen

Motivation

Right now you can use js_name to convert a Rust foo_bar into JS fooBar (or vice versa). But 99% of the time you want it to be camel case, so this just ends up being repetitive boilerplate:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_name = fooBar)]
    fn foo_bar(...);

    #[wasm_bindgen(js_name = quxCorge)]
    fn qux_corge(...);

    #[wasm_bindgen(js_name = yesNo)]
    fn yes_no(...);
}

Proposed Solution

wasm-bindgen should automatically camelify JS names, so foo_bar will be translated into fooBar on import/export.

It will still be possible to use js_name to manually override that if the user does not want camel-case.

This is a breaking change, so we should batch it together with other breaking changes.

breaking-change

Most helpful comment

Along the same lines actually, it'd be great to get this implemented today rather than waiting for a breaking change, that way the breaking change is simply switching defaults!

It'd be great if we could add #[wasm_bindgen(auto_camel_case)] or something like that as a top-level attribute which would trigger the logic for automatically case switching.

All 24 comments

Using wasm-bindgen, if I need to write some glue code in JS, I name all of them in snake case. So, this is a breaking change for me. I am very appreciate if this new feature doesn't break existing code.

@limira Of course your code won't break, because wasm-bindgen follows semver, so this would be done in a major version bump, at the same time as the other breaking changes.

It will break, eventually - because sooner or later, we have to move up to the latest version, right? That said, if you are unable to find a way to avoid the breaking, just break it - I understand the reason of this change.

Thinking about this. I wonder what others do? I started using wasm-bindgen in Jul 2018 and found it useful for real app. It's nearly one+half years ago. I chose to name functions in JS glue code in snake case to avoid using the js_name attribute. How many people do the same as me? (So, they never think about automatically camelify names, and therefore no issue was raised until this?)

That said, and as I said in the previous comment, I understand the reason of this change. I just want to deal with the breakage less painful. For example, importing JS code named in snake case:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen]
    fn foo_bar(...);

    #[wasm_bindgen]
    fn qux_corge(...);

    #[wasm_bindgen]
    fn yes_no(...);
}

Must be modified to:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_name = foo_bar)]
    fn foo_bar(...);

    #[wasm_bindgen(js_name = qux_corge)]
    fn qux_corge(...);

    #[wasm_bindgen(js_name = yes_no)]
    fn yes_no(...);
}

Doing the modification manually, it requires a lot of copy and paste. Can we do it automatically with minimal effort?

If this change will eventually happen, I guess the attribute js_name should help doing this less painful. For example, js_name = * means the JS's name is the same as in Rust. So it just requires pasting the string js_name = * to all broken imports and ... it's done.

I'd personally be in favor of this, it sounds like a great idea!

@limira we could always add support for something like this for that:

#[wasm_bindgen(disable_auto_camel_case)]
extern "C" {
    fn foo_bar(...);
    fn qux_corge(...);
    fn yes_no(...);
}

Along the same lines actually, it'd be great to get this implemented today rather than waiting for a breaking change, that way the breaking change is simply switching defaults!

It'd be great if we could add #[wasm_bindgen(auto_camel_case)] or something like that as a top-level attribute which would trigger the logic for automatically case switching.

can we consider a cli flag as an alternative?
also, are there more users of wasm-bindgen except browsers/node? Can standalone runtimes like wasmer use it?

@ibaryshnikov A CLI flag would break dependencies.

Since wasm-bindgen relies upon a JS engine, it can't really run in standalone runtimes like wasmer.

I'd like to add that I like the original idea. But there are only motivation and proposed solution fields in it. I'd like to see alternatives and drawbacks as well (and may be more on this).

A CLI flag would break dependencies.

@Pauan can you elaborate on this?

Since wasm-bindgen relies upon a JS engine

and here, too

About the latter, there are interface types and host bindings proposals, so, maybe there's more use for wasm-bindgen in the future?

@ibaryshnikov can you elaborate on this?

If you use a library (which uses wasm-bindgen), then that library will be relying on a certain case. But a CLI flag would change the casing of everything, your code and the library's code, and thus cause the library to break.

As for the JS engine part, even with interface types wasm-bindgen still needs to generate JS glue, and that will continue to be true for a long time. Plus there are things like inline_js and module.

@Pauan thanks for the explanations!

If you use a library (which uses wasm-bindgen), then that library will be relying on a certain case.

that's true, that makes cli option not usable (

even with interface types wasm-bindgen still needs to generate JS glue

In the Vision section of #1524 there's

We leverage WebIDL bindings and anyref to completely remove the wasm-bindgen-specific metadata and 99% of the JS bindings glue

Is there a section of goals and non goals for wasm-bindgen? I think it can make things a lot clearer.

Automatically changing the case looks useful, but not natural. If I get it right, using js_name changes not only the name of a function in JS but also the name of a function inside wasm. If only we could update this behavior to keep names inside wasm in snake_case and names in JS in camelCase, it might be more future-proof in case of new targets.

Finally, I'd like to propose a solution similar to serde: https://serde.rs/container-attrs.html#rename_all

#[wasm_bindgen(rename_all = "camelCase")]
extern "C" {
    fn foo_bar(...);

    fn qux_corge(...);

    fn yes_no(...);
}

If I get it right, using js_name changes not only the name of a function in JS but also the name of a function inside wasm.

That doesn't matter, since you're not importing the .wasm file, you're importing the .js file which then re-exports the wasm function (with appropriate glue code if needed).

The .wasm file contains many imports/exports which are internal implementation details, it doesn't work if you import it directly.

If only we could update this behavior to keep names inside wasm in snake_case and names in JS in camelCase, it might be more future-proof in case of new targets.

Why do you think that snake_case would be more future-proof?

In any case, wasm imports and exports are not identifiers, they are just raw UTF-8 strings, so they can be literally anything, so there is no compatibility issue (either now or in the future).

(Since it wasn't linked, here is a link to the PR for this: https://github.com/rustwasm/wasm-bindgen/pull/1823 )

That doesn't matter, since you're not importing the .wasm file, you're importing the .js file which then re-exports the wasm function (with appropriate glue code if needed).

In es modules integration proposal we can import wasm, it already works in node.js behind a flag.

Why do you think that snake_case would be more future-proof?

Only snake_case inside wasm, js glue can be anything, as it'll be removed one day (as of #1524). Becase users will not have to rename their functions like this

#[wasm_bindgen(js_name = my_function)]
pub fn my_function() {}

Can changes from #1823 also be discussed before implementing?

In es modules integration proposal we can import wasm, it already works in node.js behind a flag.

That's not what I meant, I meant that the wasm generated by wasm-bindgen isn't designed to be imported directly, and it won't work right if you try and do that. You have to import the .js file.

Only snake_case inside wasm

As I said, wasm does not use identifiers, it uses UTF-8 strings, so the imports/exports can be literally anything, they are not restricted to snake_case at all, they can contain any Unicode character.

js glue can be anything, as it'll be removed one day

As I explained, the JS glue will probably never be able to be fully removed, only reduced.

Becase users will not have to rename their functions like this

That doesn't have anything to do with the name in wasm though, that's for the JS name. The wasm name can be anything (it could even be a random auto-generated string, which is what stdweb does).

Can changes from #1823 also be discussed before implementing?

I don't see what needs to be discussed, it's a simple straightforward change that has significant advantages and no drawbacks.

But if you have any issues with it, please do mention them.

As of snake_case I have nothing to add, I'd like to hear more people here.

But if you have any issues with it, please do mention them.

We can have

#[wasm_bindgen(rename_all = camelCase)]
#[wasm_bindgen(rename_all = snake_case)]

instead of

#[wasm_bindgen(js_case)]
#[wasm_bindgen(rust_case)] // I just made it up, but what should we use in a camel-by-default world?

or instead of

#[wasm_bindgen(auto_camel_case)]
#[wasm_bindgen(disable_auto_camel_case)]

@ibaryshnikov In principle I do like that idea, but I can't think of any situation where you would want anything other than camelCase, and the extra verbosity has to be considered.

There is also an inconsistency with rename_all and js_name (which should probably be changed to rename).

On the other hand, if we go with automatic casing, then I agree that rename_all = snake_case is very nice. So I'm torn.

I'm not personally too keen on adding all the casing options supported by Serde, and I'm not really sure if there's a use case other than "camel case everything" or "turn off the auto camel case". I like the idea of just having one attribute name but we can do that multiple ways, for example #[wasm_bindgen(auto_camel_case = false)] or something like that.

If there's a compelling use case for something other than on/off of all case switching, then I think I could be persuaded for a featureful option like serde, but if there's not a use case other than "we should fill out the dots in the matrix" then I don't think we should try to support something so powerful.

@alexcrichton thanks for clarifications. Would you mind sharing some thoughts about the goals / non goals of wasm bindgen? Does it target only JS runtimes, or maybe any plans to support standalone webassembly runtimes?

Heh those sorts of goals/non-goals are constantly evolving over time. At this time wasm-bindgen isn't really built to support standalone runtimes in any way other than wasm interface types, but even then the support isn't great. I don't think there's an answer of "what does wasm-bindgen want to target?" since it's all so hypothetical and fluid today. We'll basically continue to slot wasm-bindgen in best we see and otherwise tweak design if necessary.

(I'm not sure how this is related to an attribute to automatically name names though, these seem like orthogonal concerns if any)

I'm not sure how this is related to an attribute to automatically name names though, these seem like orthogonal concerns if any

@alexcrichton here's an example
https://github.com/wasmerio/wasmer/blob/7e640c0dfb76fa70d6216e7fe7908953bdb71b8e/examples/plugin.rs#L154
If the names are automatically changed to camelCase, then

instance.func::<(i32), i32>("plugin_entrypoint")

will become

instance.func::<(i32), i32>("pluginEntrypoint")

It's not much of a problem, just may look confusing at a first glance. I'm still not sure if there will be any benefits from using wasm_bindgen with such runtime instead of a plain rustc.

@ibaryshnikov oh I think you're talking about perhaps when we change the defaults, right? That's still not a set-in-stone question I think, and adding an opt-in rename shouldn't break anyone?

@alexcrichton yes, I meant defaults. Opt-in is good to go

Also struct members would have to be renamed I suppose.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

poccariswet picture poccariswet  路  3Comments

hunterlester picture hunterlester  路  4Comments

kurbaniec picture kurbaniec  路  3Comments

derekdreery picture derekdreery  路  3Comments

fitzgen picture fitzgen  路  4Comments