{"record":{"id":"1f8f5513cba36a53","repo":"neon-bindings/neon","slug":"exported-functions-cannot-receive-self","errorCode":null,"errorMessage":"Exported functions cannot receive `self`.","messagePattern":"Exported functions cannot receive `self`\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/neon-macros/src/export/function/mod.rs","lineNumber":311,"sourceCode":"    // Extract the first argument\n    let arg = match sig.inputs.first() {\n        Some(arg) => arg,\n\n        // If context was forced, error to let the user know the mistake\n        None if opts.context => {\n            return Err(syn::Error::new(\n                sig.inputs.span(),\n                \"Expected a context argument. Try removing the `context` attribute.\",\n            ))\n        }\n\n        None => return Ok(None),\n    };\n\n    // Expect a typed pattern; self receivers are not supported\n    match arg {\n        syn::FnArg::Typed(ty) => Ok(Some(ty)),\n        syn::FnArg::Receiver(arg) => Err(syn::Error::new(\n            arg.span(),\n            \"Exported functions cannot receive `self`.\",\n        )),\n    }\n}\n\nfn is_context_type(ty: &syn::Type) -> bool {\n    let ident = match type_path_ident(ty) {\n        Some(ident) => ident,\n        None => return false,\n    };\n\n    ident == \"FunctionContext\" || ident == \"Cx\"\n}\n\nfn is_channel_type(ty: &syn::Type) -> bool {\n    let ident = match type_path_ident(ty) {\n        Some(ident) => ident,","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/neon-bindings/neon/blob/38960e4381d9ad13b551cdf2d261f609167c9bc2/crates/neon-macros/src/export/function/mod.rs#L293-L329","documentation":"The `#[neon::export]` macro refuses Rust functions whose first parameter is a `self` receiver (e.g. `fn foo(&self)`). Exported functions become plain JavaScript-callable functions, so they have no object instance to dispatch against; the macro's `first_arg` check inspects the first argument and rejects `FnArg::Receiver`.","triggerScenarios":"Applying `#[neon::export]` (via `check_context`/`check_channel` path) to an inherent or trait method that takes `self`, `&self`, `&mut self`, or `self` as its first parameter.","commonSituations":"Refactoring an existing Rust struct method to be exported by just adding the attribute; writing `impl MyStruct { #[neon::export] fn create(&self) {...} }`; confusing Rust methods with the macro's supported standalone functions, consts, statics, and impl blocks.","solutions":["Remove the `self` parameter and make the exported item a free function, taking needed values as explicit arguments","If exporting a method-like API, export a free constructor function and call methods internally, or use a class (impl block) export instead","Move the attribute off the method and export a wrapper free function that constructs the receiver","Use plain `#[napi]`-style conventions: export only free functions with JS-visible argument types"],"exampleFix":"// before\nstruct Counter { n: u32 }\nimpl Counter {\n    #[neon::export]\n    fn increment(&self) -> u32 { self.n + 1 }\n}\n// after\nstruct Counter { n: u32 }\n#[neon::export]\nfn increment(n: f64) -> f64 { n + 1.0 }","handlingStrategy":"validation","validationCode":"function assertExportableFn(fn) { if (typeof fn !== 'function') throw new TypeError('export target must be a free function'); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only annotate free functions (no self receivers) with #[neon::export]","Check the compile error location: it points at the receiver argument; remove self or restructure","Prefer exporting impl blocks (classes) for instance-style APIs rather than methods with #[neon::export]"],"tags":["rust","neon","macros","export","self-receiver"],"backgroundTag":"unsupported-operation","analyzedSha":"38960e4381d9ad13b551cdf2d261f609167c9bc2","analyzedAt":"2026-09-13T09:05:33.640Z","contentChangedAt":"2026-09-13T09:05:33.640Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}