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

Self pointer need to be the first

Error message

Self pointer need to be the first

What it means

When a vtable trait method takes self (VRef<Self>, VRefMut<Self> or &Self), that self-like pointer must be the first parameter so the macro can generate the correct dispatch thunks. If inputs precede the self parameter, the macro emits this compile error at the offending parameter.

Solutions

  1. Reorder so the self-like parameter (VRef, VRefMut, &Self) comes first.
  2. Pass extra context via the instance itself or via additional trailing parameters.

Example fix

// before
fn draw(ctx: &RenderContext, self_: VRef<MyVTable>);
// after
fn draw(self_: VRef<MyVTable>, ctx: &RenderContext);
Defensive patterns

Strategy: validation

Validate before calling

// self-like parameter (VRef/VRefMut/&Self) must be first
fn draw(self_: VRef<MyVTable>, ctx: &Ctx);

Prevention

When it happens

Trigger: Writing e.g. `fn draw(ctx: &Ctx, self_: VRef<MyVTable>)` — any trait method where a VRef/VRefMut/&Self parameter is not first.

Common situations: Porting from trait syntax where `self` is implicit and adding explicit arguments before it; designing methods with a context/state parameter first out of habit from other frameworks.

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/ae1e540185398313. Report an issue: GitHub.

Appendix: source

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

                    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 {
                    (false, None)
                } {
                    if !sig.inputs.is_empty() {
                        return Error::new(param.span(), "Self pointer need to be the first")
                            .to_compile_error()
                            .into();
                    }

                    let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
                    has_self = true;
                    if !is_pin {
                        sig.inputs.push(FnArg::Receiver(Receiver {
                            attrs: param.attrs.clone(),
                            mutability: None,
                            self_token: Default::default(),
                            kind: ReceiverKind::Reference(Default::default(), None, mutability),
                        }));
                        call_code =
                            Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
                        self_call =
                            Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
                    } else {

View on GitHub (pinned to bb937076de)