slint-ui/slint · error · proc_macro::Error
The structure does not ends in 'VTable'
Error message
The structure does not ends in 'VTable'
What it means
The #[vtable] macro derives the trait name by stripping a literal 'VTable' suffix from the struct's identifier (e.g. FooVTable -> trait Foo). A struct whose name does not end in 'VTable' breaks this derivation, so the macro rejects it at compile time.
Solutions
- Rename the struct so it ends in 'VTable' (e.g. MyTraitVTable for trait MyTrait).
- Ensure the part before 'VTable' is exactly the trait name you want, since it is used verbatim to generate the trait.
Example fix
// before
#[vtable]
struct MyTraitVT { init: fn(VRef<MyTraitVT>) }
// after
#[vtable]
struct MyTraitVTable { init: fn(VRef<MyTraitVTable>) } Defensive patterns
Strategy: validation
Validate before calling
assert!(struct_name.ends_with("VTable")); Prevention
- Follow the naming convention TraitNameVTable for every #[vtable] struct.
- Check the struct name after any rename refactor.
When it happens
Trigger: Annotating a named-field struct named e.g. `FooVt`, `FooTable`, or `Foo` with #[vtable].
Common situations: Renaming a vtable struct during refactoring and accidentally removing or altering the required 'VTable' suffix; inventing a new trait interface without following the naming convention.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Only supported for structure with named fields
- Self pointer need to be the first
- The type of an #[field_offset] member in the vtable must be…
- VTable cannot be mutable
- VTable pointer need to be the first
AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16).
Data as JSON: /api/errors/9c772148d47551fe.
Report an issue: GitHub.
Appendix: source
Thrown at helper_crates/vtable/macro/macro.rs:183
*/
#[proc_macro_attribute]
pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as ItemStruct);
let fields = if let Fields::Named(fields) = &mut input.fields {
fields
} else {
return Error::new(
proc_macro2::Span::call_site(),
"Only supported for structure with named fields",
)
.to_compile_error()
.into();
};
let vtable_name = input.ident.to_string();
if !vtable_name.ends_with("VTable") {
return Error::new(input.ident.span(), "The structure does not ends in 'VTable'")
.to_compile_error()
.into();
}
let trait_name = Ident::new(&vtable_name[..vtable_name.len() - 6], input.ident.span());
let to_name = quote::format_ident!("{}TO", trait_name);
let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
let static_vtable_macro_name = quote::format_ident!("{}_static", vtable_name);
let vtable_name = input.ident.clone();
let mut drop_impls = Vec::new();
let mut generated_trait = ItemTrait {
attrs: input
.attrs
.iter()
.filter(|a| a.path().get_ident().as_ref().map(|i| *i == "doc").unwrap_or(false))View on GitHub (pinned to bb937076de)