Working with Duck-Typed Interfaces

Liberal use of the structural attribute on imported methods, getters, and setters allows you to define duck-typed interfaces. A duck-typed interface is one where many different JavaScript objects that don't share the same base class in their prototype chain and therefore are not instanceof the same base can be used the same way.

Defining a Duck-Typed Interface in Rust

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

/// Here is a duck-typed interface for any JavaScript object that has a `quack`
/// method.
///
/// Note that any attempts to check if an object is a `Quacks` with
/// `JsCast::is_instance_of` (i.e. the `instanceof` operator) will fail because
/// there is no JS class named `Quacks`.
#[wasm_bindgen]
extern "C" {
    pub type Quacks;

    #[wasm_bindgen(structural, method)]
    pub fn quack(this: &Quacks) -> String;
}

/// Next, we can export a function that takes any object that quacks:
#[wasm_bindgen]
pub fn make_em_quack_to_this(duck: &Quacks) {
    let _s = duck.quack();
    // ...
}
}

JavaScript Usage

import { make_em_quack_to_this } from "./rust_duck_typed_interfaces";

// All of these objects implement the `Quacks` interface!

const alex = {
  quack: () => "you're not wrong..."
};

const ashley = {
  quack: () => "<corgi.gif>"
};

const nick = {
  quack: () => "rappers I monkey-flip em with the funky rhythm I be kickin"
};

// Get all our ducks in a row and call into wasm!

make_em_quack_to_this(alex);
make_em_quack_to_this(ashley);
make_em_quack_to_this(nick);