slint-ui/slint · error · proc_macro::Error

VTable pointer need to be the first

Error message

VTable pointer need to be the first

What it means

The macro treats a first parameter pointing to the vtable type as the implicit dispatch/self-slot and requires it to be the ONLY first parameter: it must appear before any other parameters, and the function must not be a #[call] entry (call_code). Encountering the vtable-pointer type in any other position, or in a function that already has a call target or other inputs, produces this compile error.

Solutions

  1. Move the *const MyVTable parameter to be the first parameter of the method.
  2. Remove extra parameters before the vtable pointer, or drop the vtable pointer if it is not needed.
  3. Do not combine the vtable-pointer form with a #[call] designated function.

Example fix

// before
fn dispatch(arg: u32, vt: *const MyVTable);
// after
fn dispatch(vt: *const MyVTable, arg: u32);
Defensive patterns

Strategy: validation

Validate before calling

// vtable pointer must be parameter #1 and not in the #[call] fn
fn dispatch(vt: *const MyVTable, /* other args */);

Prevention

When it happens

Trigger: Placing a `*const MyVTable` parameter after other arguments in a trait method signature; using the vtable-pointer form in the #[call] function which already has call_code set.

Common situations: Reordering parameters when adding context arguments; misunderstanding that the vtable pointer is positional and must lead the signature.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16). Data as JSON: /api/errors/fe37704215716218. Report an issue: GitHub.

Appendix: source

Thrown at helper_crates/vtable/macro/macro.rs:318

                        Some((matches!(mutability, PointerMutability::Mut(_)), elem))
                    }
                    Type::Reference(TypeReference { mutability, elem, .. }) => {
                        Some((mutability.is_some(), elem))
                    }
                    _ => None,
                };
                if let Some((is_mut, elem)) = ptr_target
                    && let Type::Path(p) = &**elem
                    && let Some(pointer_to) = p.path.get_ident()
                    && pointer_to == &vtable_name
                {
                    if is_mut {
                        return Error::new(p.span(), "VTable cannot be mutable")
                            .to_compile_error()
                            .into();
                    }
                    if call_code.is_some() || !sig.inputs.is_empty() {
                        return Error::new(p.span(), "VTable pointer need to be the first")
                            .to_compile_error()
                            .into();
                    }
                    call_code = Some(quote!(vtable as _,));
                    continue;
                }

                let (is_pin, self_ty) = match is_pin(&param.ty) {
                    Some(t) => (true, t),
                    None => (false, &param.ty),
                };

                // check for self
                if let (true, mutability) = if match_generic_type(self_ty, "VRef", &vtable_name) {
                    (true, None)
                } else if match_generic_type(self_ty, "VRefMut", &vtable_name) {
                    (true, Some(Default::default()))
                } else {

View on GitHub (pinned to bb937076de)