crustinteropexternwasm-bindgen

How does the Rust compiler make sense of log_u32 and log_many in the wasm-bindgen example below?


The following code is taken from the wasm-bindgen tutorial.

My question pertains to the code block that begins with extern "C" and contains the functions log_u32 and log_many.

From what I understand, calling extern "C" lets you use C functions in Rust. For example, log here is simply the log function from C, if I understand correctly.

But, here's my issue: That code block also contains the functions log_u32 and log_many. But there are no C functions named log_u32 and log_many that I'm aware of. So where do these two functions get their definitions? How does the Rust compiler make sense of them?

#![allow(unused_variables)]
fn main() {
use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn run() {
    bare_bones();
    using_a_macro();
    using_web_sys();
}

// First up let's take a look of binding `console.log` manually, without the
// help of `web_sys`. Here we're writing the `#[wasm_bindgen]` annotations
// manually ourselves, and the correctness of our program relies on the
// correctness of these annotations!

#[wasm_bindgen]
extern "C" {
    // Use `js_namespace` here to bind `console.log(..)` instead of just
    // `log(..)`
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    // The `console.log` is quite polymorphic, so we can bind it with multiple
    // signatures. Note that we need to use `js_name` to ensure we always call
    // `log` in JS.
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_u32(a: u32);

    // Multiple arguments too!
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_many(a: &str, b: &str);
}

fn bare_bones() {
    log("Hello from Rust!");
    log_u32(42);
    log_many("Logging", "many values!");
}

// Next let's define a macro that's like `println!`, only it works for
// `console.log`. Note that `println!` doesn't actually work on the wasm target
// because the standard library currently just eats all output. To get
// `println!`-like behavior in your app you'll likely want a macro like this.

macro_rules! console_log {
    // Note that this is using the `log` function imported above during
    // `bare_bones`
    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}

fn using_a_macro() {
    console_log!("Hello {}!", "world");
    console_log!("Let's print some numbers...");
    console_log!("1 + 3 = {}", 1 + 3);
}

// And finally, we don't even have to define the `log` function ourselves! The
// `web_sys` crate already has it defined for us.

fn using_web_sys() {
    use web_sys::console;

    console::log_1(&"Hello using web-sys".into());

    let js: JsValue = 4.into();
    console::log_2(&"Logging arbitrary values looks like".into(), &js);
}

}

Solution

  • It's really simple, you tell it what function to use with js_name = log it uses the log function.

    In JavaScript that function is polymorphic and can handle different types of input, since there are no polymorphic functions in Rust to satisfy the compiler whe have to give different names to the different overloaded versions of log.