Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
filonik opened this issue Oct 9, 2022 · 4 comments · May be fixed by #3631
Open

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

filonik opened this issue Oct 9, 2022 · 4 comments · May be fixed by #3631
Labels

Comments

@filonik
Copy link

filonik commented Oct 9, 2022

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.

@felixnaredi
Copy link

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
  }
}

@filonik
Copy link
Author

filonik commented Oct 11, 2022

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.

@Liamolucko
Copy link
Collaborator

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.

@filonik
Copy link
Author

filonik commented Oct 12, 2022

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?

@snOm3ad snOm3ad linked a pull request Sep 23, 2023 that will close this issue
@daxpedda daxpedda added bug and removed question labels Sep 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants