How to Fix: `component-model`: export function named `new` in `.wit` causes name conflict
When a WIT export is named new, wasmtime::component::bindgen! can generate Rust that collides with reserved constructor-style naming, causing a binding generation failure instead of valid component glue code.
This issue appears when a .wit interface exports a function literally named new. Although that identifier is valid in the interface definition, it becomes problematic during Rust code generation because the generated bindings must map WIT names into Rust items, and new has special semantic weight in Rust APIs and generator conventions.
Problem Overview
The failing setup looks like this:
interface test {
new: func()
}
world test-world {
default export test
}
And the Rust side:
wasmtime::component::bindgen!({
path: "test.wit",
});
The conflict happens during binding generation. Instead of safely emitting an escaped Rust identifier or applying a naming transformation, the generated code runs into a name conflict around new.
In practice, the most reliable fix is to rename the exported WIT function to something more explicit and less collision-prone, such as create, init, or make.
Understanding the Root Cause
This happens because WIT identifiers and Rust generated bindings do not share identical naming rules or conventions.
Here is the technical breakdown:
- In WIT, a function named new is syntactically reasonable.
- In Rust, new is not a reserved keyword, but it is a heavily used conventional constructor name.
- wasmtime component bindgen transforms WIT exports into Rust types, traits, methods, or helper items.
- During that transformation, the generator may need to emit items where new conflicts with an already generated symbol, expected constructor helper, or implementation structure.
- The result is a binding-generation naming collision, not necessarily a parser error in WIT itself.
So the root cause is not that WIT forbids new. The issue is that the code generator’s Rust naming layer cannot always represent that export unambiguously in the generated bindings.
This is a classic example of a cross-language ABI/tooling mismatch: a legal source identifier in an interface definition becomes unsafe when projected into a target language with different conventions and generator internals.
Step-by-Step Solution
The cleanest solution is to rename the exported function in the .wit file and regenerate bindings.
1. Rename the WIT export
Change this:
interface test {
new: func()
}
world test-world {
default export test
}
To this:
interface test {
create: func()
}
world test-world {
default export test
}
You can also use names like:
- init
- make
- instantiate
- open
Choose a name that matches the function’s real behavior rather than relying on the overloaded meaning of new.
2. Regenerate the Rust bindings
wasmtime::component::bindgen!({
path: "test.wit",
});
If your project builds generated bindings during compilation, run a normal rebuild:
cargo clean
cargo build
This ensures any stale generated artifacts are removed before the new interface is processed.
3. Update implementation code that referenced the old export
If your Rust implementation or host integration referred to the old exported name conceptually, update those call sites or trait implementations to match the renamed WIT function.
For example, after renaming to create, your generated interfaces will typically expose symbols aligned with that new name instead of new.
4. Prefer stable, generator-friendly naming conventions
For exported WIT functions intended to map cleanly into multiple target languages, prefer verbs that are:
- descriptive
- unlikely to collide
- not overloaded by language conventions
Good examples:
interface test {
initialize: func()
create_instance: func()
open_session: func()
}
5. If you must preserve the external API name
If compatibility requirements force the public interface to remain new, then the real fix must happen in the tooling itself: wasmtime’s bindgen should escape, remap, or otherwise disambiguate that identifier during Rust code generation.
In that case, your options are:
- Patch or upgrade the relevant wasmtime version if a fix exists.
- Track the upstream issue and changelog.
- Temporarily maintain a local fork if the API surface cannot change.
But for most application teams, renaming the WIT export is the fastest and safest workaround.
Working example
interface test {
create: func()
}
world test-world {
default export test
}
wasmtime::component::bindgen!({
path: "test.wit",
});
This avoids the naming conflict while preserving the same overall component structure.
Common Edge Cases
1. Other problematic function names
Even if new is the immediate failure, other names may also be risky depending on generator behavior, target language, or macro expansion. Watch for names that overlap with:
- Rust keywords
- common trait method names
- generated helper symbols
- constructor-like conventions
Examples worth reviewing carefully include default, clone, drop, and from.
2. Renaming can break downstream consumers
If other components or host code already depend on the original WIT contract, renaming new to create is a breaking interface change. In shared ecosystems, coordinate the update and version the interface appropriately.
3. Stale generated artifacts
Sometimes the rename is correct, but the build still fails because old generated code remains cached. A full rebuild with cargo clean is often enough to resolve this.
4. Mixed toolchain versions
If your team uses different versions of wasmtime, wit-parser, or related component-model tooling, one developer may reproduce the issue while another cannot. Keep versions pinned and documented.
5. Imported versus exported interfaces
The bug report is specifically about an export function named new. Similar naming behavior may differ for imports, generated traits, or resource-related bindings. Test both directions if your interface is symmetric.
FAQ
Is new illegal in WIT?
No. The problem is not the WIT syntax itself. The issue arises when bindgen maps that identifier into generated Rust bindings and hits a naming conflict.
Why doesn’t Rust just allow it if new is not a reserved keyword?
Because the failure is usually not about Rust parsing the word alone. It is about generated symbol layout, method naming conventions, or collisions with other generated items in the binding layer.
What is the best long-term fix: rename the function or wait for tooling changes?
If you control the interface, rename the function. That is the fastest production-safe fix. If API compatibility is mandatory, then the long-term fix belongs upstream in the code generator, which should escape or remap conflicting names automatically.
For reference on evolving support in the component ecosystem, monitor the relevant Wasmtime project and related Component Model documentation.