wasm-bindgen icon indicating copy to clipboard operation
wasm-bindgen copied to clipboard

Returning Option<Self> in #[wasm_bindgen] impl block gives error

Open filonik opened this issue 3 years ago • 4 comments

Summary

This may not be the best example, but for simplicity I am adapting the code from this issue. The problem is as follows:

The following return types work for new with the #[wasm_bindgen] attribute applied to the impl: Universe, Self, Option<Universe>. However, this return type appears not to work: Option<Self>. Is there some deep reason for this, or is this potentially a bug?

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cell {
    Dead = 0,
    Alive = 1,
}

#[wasm_bindgen]
pub struct Universe {
    width: u32,
    height: u32,
    cells: Vec<Cell>,
}

/// Public methods, exported to JavaScript.
#[wasm_bindgen]
impl Universe {
    // The tutorial has the signature:
    // pub fn new() -> Universe {
    // This signature works:
    // pub fn new() -> Option<Universe> {
    pub fn new() -> Option<Self> {
        let width = 128;
        let height = 128;

        let cells = (0..width * height)
            .map(|i| {
                if i % 2 == 0 || i % 7 == 0 {
                    Cell::Alive
                } else {
                    Cell::Dead
                }
            })
            .collect();

        Some(Self {
            width,
            height,
            cells,
        })
    }
}

This gives the error:

error[E0401]: can't use generic parameters from outer function
   --> src\lib.rs:177:28
    |
139 | impl Universe {
    | ---- `Self` type implicitly declared here, by this `impl`
...
177 |     pub fn new() -> Option<Self> {
    |                            ^^^^
    |                            |
    |                            use of generic parameter from outer function
    |                            use a type here instead

The same problem arises when attempting to use Self with Result and other generics.

filonik avatar Oct 09 '22 22:10 filonik

The error that arises is the following: E0401: Inner items do not inherit type or const parameters from the functions they are embedded in.. It hints at that an outer type parameter (in this case Self) is being used in an embedded function.

But how can that be when there isn't any inner function? Well, maybe there is. Lets take a look at this even simpler example:

#[wasm_bindgen]
pub struct A();

#[wasm_bindgen]
impl A
{
  #[wasm_bindgen]
  pub fn foo() -> Option<Self>
  {
    None
  }
}

Using cargo-extend we can see what the #[wasm_bindgen] macro does to the function foo with .

impl A {
    pub fn foo() -> Option<Self> {
        #[automatically_derived]
        const __wasm_bindgen_generated_A_f__const: () = {
            pub unsafe extern "C" fn __wasm_bindgen_generated_A_f() -> <Option<
                Self,
            > as wasm_bindgen::convert::ReturnWasmAbi>::Abi {
                let _ret = {
                    let _ret = A::f();
                    _ret
                };
                <Option<Self> as wasm_bindgen::convert::ReturnWasmAbi>::return_abi(_ret)
            }
        };
        None
    }
}

And there you have it. The problem is that the #[wasm_bindgen] macro creates an inner function with a return type that references Self. Which is disallowed according to E0401:

pub unsafe extern "C" fn __wasm_bindgen_generated_A_f() -> <Option<
                Self,
            >

An example that recreates the error without the #[wasm_bindgen] macro could look like this:

pub struct A();

impl A
{
  pub fn foo() -> Option<Self>
  {
    // `bar` is not allowed to reference `Self`.
    fn bar() -> Option<Self> {
      None
    }
    None
  }
}

felixnaredi avatar Oct 11 '22 19:10 felixnaredi

Thank you for the detailed answer. It sounds like this is expected (at least if you are familiar with #[wasm_bindgen] internals) and there is no viable path to mitigating this issue in the near term. I suppose explicitly spelling out the type is the best Option then.

filonik avatar Oct 11 '22 22:10 filonik

It sounds like this is expected (at least if you are familiar with #[wasm_bindgen] internals) and there is no viable path to mitigating this issue in the near term.

No, I think this should be fixable. #[wasm_bindgen] has access to the name of the struct, so it could automatically replace all instances of Self with the concrete type to fix this.

Liamolucko avatar Oct 12 '22 01:10 Liamolucko

That is great to hear! It would certainly remove a sharp corner for newcomers like myself to cut themselves on... 😅

Should the label be changed from question to enhancement then?

filonik avatar Oct 12 '22 08:10 filonik