{"record":{"id":"a8b1cd34a48b8874","repo":"oven-sh/bun","slug":"loc-host-export-e-symbol-rust-param-p","errorCode":null,"errorMessage":"${loc}: HOST_EXPORT(${e.symbol}, rust) param `${p.raw}` is a reference; use a raw pointer","messagePattern":"(.+?): HOST_EXPORT\\((.+?), rust\\) param `(.+?)` is a reference; use a raw pointer","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/codegen/generate-host-exports.ts","lineNumber":422,"sourceCode":"      // `SYSV_ABI` lazyPropCb-style getters). `&JSGlobalObject` is\n      // ABI-identical to non-null `*const JSGlobalObject`; C++ never passes\n      // null here.\n      const body = retIsJsResult\n        ? `    host_fn::host_fn_lazy(g, ${impl})`\n        : `    host_fn::host_fn_lazy_passthrough(g, ${impl})`;\n      return `\n// ${loc}\n${emitNoMangle(e.abi, e.symbol, \"g: &JSGlobalObject\", \"JSValue\", body)}`;\n    }\n    case \"rust\": {\n      // `extern \"Rust\"` link-time hook: forward the safe signature verbatim\n      // (no pointer rewriting; `extern \"Rust\"` ABI == native Rust ABI).\n      // Reference params/returns are rejected — `extern \"Rust\" fn` items need\n      // explicit lifetimes for borrowed types and the generator does not\n      // synthesise them. Use raw pointers in the impl signature instead.\n      for (const p of e.params)\n        if (p.ty.startsWith(\"&\"))\n          throw new Error(\n            `${loc}: HOST_EXPORT(${e.symbol}, rust) param \\`${p.raw}\\` is a reference; use a raw pointer`,\n          );\n      if (e.ret.startsWith(\"&\"))\n        throw new Error(\n          `${loc}: HOST_EXPORT(${e.symbol}, rust) return type \\`${e.ret}\\` is a reference; use a raw pointer`,\n        );\n      const sig = e.params.map(p => `${p.name}: ${p.ty}`).join(\", \");\n      const call = e.params.map(p => p.name).join(\", \");\n      return `\n// ${loc}\n#[allow(dead_code, unreachable_pub, unused)]\n#[unsafe(no_mangle)]\npub extern \"Rust\" fn ${e.symbol}(${sig}) -> ${e.ret} {\n    ${impl}(${call})\n}`;\n    }\n    case \"generic\": {\n      const sig = e.params.map(p => `${p.name}: ${p.cTy}`).join(\", \");","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/codegen/generate-host-exports.ts#L404-L440","documentation":"For hooks declared `HOST_EXPORT(symbol, rust)`, the generator forwards the signature verbatim into an `#[unsafe(no_mangle)] pub extern \"Rust\" fn` item. Reference parameters are rejected because an extern \"Rust\" fn item requires explicit lifetimes for borrowed types, which the generator does not synthesize. The fix is always the same: take a raw pointer (`*const T` / `*mut T`) at the ABI boundary and dereference inside.","triggerScenarios":"Writing `// HOST_EXPORT(MyHook, rust)` above a `pub fn` whose parameters include `&JSGlobalObject`, `&str`, `&T`, or `&mut T`.","commonSituations":"Adding new link-time Rust hooks (the `rust` linkage variant, as opposed to `jsc`/`c`) and copying the safe signature from a regular function; the jsc/c variants tolerate references because thunks are generated, the rust variant does not.","solutions":["Change each reference parameter to a raw pointer: `&T` → `*const T`, `&mut T` → `*mut T`","Reborrow inside the body: `let r = unsafe { &*ptr };` (or use the established Bun helpers for JSGlobalObject pointers)","If the hook must stay reference-typed, use the `jsc` or `c` variant, which go through a generated thunk that rewrites pointers"],"exampleFix":"// before\n// HOST_EXPORT(BunOnHook, rust)\npub fn on_hook(global: &JSGlobalObject, cb: &JSValue) { ... }\n\n// after\n// HOST_EXPORT(BunOnHook, rust)\npub fn on_hook(global: *const JSGlobalObject, cb: *const JSValue) { ... }","handlingStrategy":"validation","validationCode":"// reject reference params on HOST_EXPORT(..., rust) hooks\nimport { Glob } from \"bun\";\nfor (const f of new Glob(\"src/**/*.rs\").scanSync(\".\")) {\n  const text = await Bun.file(f).text();\n  const re = /\\/\\/\\s*HOST_EXPORT\\(\\s*(\\w+)\\s*,\\s*rust\\s*\\)[^;]*?pub\\s+fn\\s+\\1\\s*\\(([^)]*)\\)/gs;\n  for (const m of text.matchAll(re)) {\n    if (m[2].split(\",\").some(p => p.trim().replace(/^[\\w\\s]+:\\s*/, \"\").startsWith(\"&\"))) {\n      throw new Error(`${f}: rust hook ${m[1]} takes a reference — use a raw pointer`);\n    }\n  }\n}","typeGuard":"function isRawPointerTy(ty: string): boolean {\n  return !ty.trim().startsWith(\"&\");\n}","tryCatchPattern":null,"preventionTips":["Reserve reference-typed signatures for the jsc/c HOST_EXPORT variants that generate pointer-rewriting thunks","For rust-linkage hooks, default to `*const T`/`*mut T` params and deref inside the body"],"tags":["ffi","codegen","rust","abi","host-exports"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}