Rust in emscripten. Error: Attempt to use a moved value

A disclaimer: I am a total noob on Rust. I thought that the best way for me to get into it, is to integrate into a project of my own.

I do not know how to debug this. The rust code compiles. I get a runtime "Attempt to use a moved value" on the web browser's console.log. I suppose I am misusing ownership somehow from JS.

I created a stripped down version of the project just to demonstrate this error in context, if anyone wants to see:
github /autotel/rust-audio-in-browser-minimum
(sorry, the forum wouldn't let me post more than 2 links)

These are the lines where I expect the mistake to be:

Lib.rs:

The structs that are compiled with emscripten and instanced in a web audio worklet.
context: rust-audio-in-browser-minimum/lib.rs at main · autotel/rust-audio-in-browser-minimum · GitHub


#[wasm_bindgen]
pub struct PolyManager {
    max_voices: usize,
    last_stolen_voice: usize,
}
#[wasm_bindgen]
impl PolyManager {
    pub fn new() -> PolyManager {
        log("Rust: new PolyManager");
        utils::set_panic_hook();

        let ret = PolyManager {
            max_voices: 32,
            last_stolen_voice: 0,
        };

        ret
    }
    pub fn trig(&mut self, freq: f32, amp: f32) {
        // does a thing
    }
    pub fn get_dummy_block(self, block_size: usize) -> Vec<f32> {
        let mut mix = Vec::new();
        for sample_n in 0..block_size {
            let f = 220.;
            let sf = SAMPLING_RATE as f32 / f;
            // let rand: f32 = rand::random(); // Also got problems with rand. Left that for later.
            let rand: f32 = ((sample_n as f32 / sf) % 1.) - 0.5; // using sawtooth instead.
            mix.push(rand);
        }
        mix
    }
}

MagicEngineWorklet.js

the js worklet that invokes the rust module.
in context: rust-audio-in-browser-minimum/MagicEngine.js at main · autotel/rust-audio-in-browser-minimum · GitHub

class MagicWorklet extends AudioWorkletProcessor {
    /** @type {PolyManager|null} */
    rustSynthesizer = null;
    /** @type {Object|null} */
    webAssemblyInstance = null;
    constructor() {
        super();

        console.log("magic-worklet instanced");
        this.samples = [];
        this.totalSamples = 0;


        this.port.onmessage = ({ data: { type, data, wasmBytes } }) => {
            if (halted) return console.log("halted");

            const importObject = {
                env: {},
            };
            
            // loading WASM into a worker is a huge PITA
            // https://github.com/the-drunk-coder/ruffbox/blob/master/js/worklet.js
            // https://steemit.com/eos/@skenan/eos-development-for-beginners-webassembly
            // https://github.com/Ameobea/web-synth/blob/main/public/WaveTableNodeProcessor.js

            if (type === "load-wasm") {
                console.log("worklet: load-wasm received", wasmBytes);
                init(WebAssembly.compile(wasmBytes)).then(() => {
                    console.log({ PolyManager });
                    this.rustSynthesizer = PolyManager.new();
                    // this.rustSynthesizer = new PolyManager();
                    console.log({rustSynthesizer:this.rustSynthesizer});
                    return this.port.postMessage({ type: "wasm-loaded" });
                })
            } else if (type == "trig") {
                if (!this.rustSynthesizer) return console.log("not isntanced");
                console.log("worklet:", data);
                const freq = 55 * Math.pow(2, data.note / 12);
                try {
                    this.rustSynthesizer.trig(freq, 1);
                    console.log("trigger succesful");
                } catch (e) {
                    devRustErrorHandler();
                    console.error(e);
                }
            }
        };
    }



    process(inputs, outputs, parameters) {
        if (!this.rustSynthesizer) {
            console.log("not ready");
            return true;
        }
        const firstOutput = outputs[0];
        /** @type {number} */
        const blockSize = firstOutput[0].length;
        try {
            // const mix = this.rustSynthesizer.get_block(blockSize);
            const mix = this.rustSynthesizer.get_dummy_block(blockSize);
            firstOutput.forEach(
                /**
                 * @param {Float32Array} channel
                 * @param {number} channelN
                 */
                (channel, channelN) => {
                    channel.set(mix)
                }
            )
        } catch (e) {
            console.log("audioblock worklet error", e);
        }

        return true;
    }
}


registerProcessor("magic-worklet", MagicWorklet);

The error

sorry for naming the lib as rrr :stuck_out_tongue:

audioblock worklet error Error: Attempt to use a moved value
    at PolyManager.get_dummy_block (rrr.js:200:38)
    at MagicWorklet.process (MagicEngineWorklet.js?t=1649581669029:88:46)

Many thanks! :slight_smile:

get_dummy_block consumes self
Probably here should be &self as argument.

Thanks for checking!

Does this mean that one can only call static functions from javascript?

How should I call this get_block function from Javascript? I will need to use self in the function, as it will read data that is derived from it's own properties! (I forgot to make that fact evident in the example code)

The question is, do you need to use self (i.e. consume ownership) or &self (i.e. borrow it for the call)?

ah! hmm... now I know that I don't really understand ownership :sweat_smile:

thanks you both! :slight_smile:

Works:

use wasm_bindgen::prelude::*;
static SAMPLING_RATE: i32 = 44100;
mod utils;
extern crate rand;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
    // 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);
}

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, this is rust speaking.");
}

#[wasm_bindgen]
pub struct PolyManager {
    max_voices: usize,
    last_stolen_voice: usize,
    dummy_frequency: f32,
}
#[wasm_bindgen]
impl PolyManager {
    pub fn new() -> PolyManager {
        log("Rust: new PolyManager");
        utils::set_panic_hook();

        let ret = PolyManager {
            max_voices: 32,
            last_stolen_voice: 0,
            dummy_frequency: 220.,
        };

        ret
    }
    pub fn trig(&mut self, freq: f32, amp: f32) {
        // does a thing
    }
    pub fn get_dummy_block(&self, block_size: usize) -> Vec<f32> {
        let mut mix = Vec::new();
        for sample_n in 0..block_size {
            let f = self.dummy_frequency;
            let sf = SAMPLING_RATE as f32 / f;
            // let rand: f32 = rand::random(); // Also got problems with rand. Left that for later.
            let rand: f32 = ((sample_n as f32 / sf) % 1.) - 0.5; // using sawtooth instead.
            mix.push(rand);
        }
        mix
    }
}