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
- Change the vtable pointer parameter to *const MyVTable.
- 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
- Always use *const for vtable pointer parameters.
- Use VRefMut<Self> (not a mutable vtable pointer) when you need mutable access to the instance.
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
- Only supported for structure with named fields
- Self pointer need to be the first
- The structure does not ends in 'VTable'
- The type of an #[field_offset] member in the vtable must be…
- VTable pointer need to be the first
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 ¶m.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(¶m.ty) {
Some(t) => (true, t),
None => (false, ¶m.ty),
};
// check for selfView on GitHub (pinned to bb937076de)