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

VTable cannot be mutable

Error message

VTable cannot be mutable

What it means

In a #[vtable] trait method, the first parameter may be a *const pointer to the vtable itself (the dispatch entry). The macro forbids declaring that vtable pointer as *mut, because vtables are shared immutable dispatch tables; mutating one would be undefined behavior.

Solutions

  1. Change the vtable pointer parameter to *const MyVTable.
  2. If you intended mutability of the instance, use VRefMut<Self> as the first parameter instead of a vtable pointer.

Example fix

// before
fn dispatch(vt: *mut MyVTable);
// after
fn dispatch(vt: *const MyVTable);
Defensive patterns

Strategy: validation

Validate before calling

// signature check: vtable ptr must be *const
fn dispatch(vt: *const MyVTable);

Prevention

When it happens

Trigger: Declaring a vtable method whose first argument is `*mut MyVTable` (or &mut) instead of `*const MyVTable`.

Common situations: Writing 'mutable-looking' method signatures by habit when defining raw-pointer vtable methods; confusing the self-pointer parameter (VRefMut) with the vtable pointer parameter.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

                sig_extern.inputs.push(typed_arg.clone());

                // check for the vtable
                let ptr_target = match &param.ty {
                    Type::Ptr(TypePtr { mutability, elem, .. }) => {
                        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

View on GitHub (pinned to bb937076de)