linebender/druid · error

Lens implementations can only be derived from structs with…

Error message

Lens implementations can only be derived from structs with named fields

What it means

The Lens derive requires a struct whose fields are declared with names (`struct Foo { a: u32 }`). When `derive_struct` in druid-derive is handed a struct whose fields are unnamed (tuple struct) or a unit struct, the parsed Fields::<LensAttrs> cannot be produced and the macro fails with this message at input.span().

Solutions

  1. Convert the tuple struct to a struct with named fields, e.g. `struct Wrapper { inner: T }`.
  2. If fields must stay unnamed, remove the derive and write a manual `impl Lens` for the type.
  3. For a unit struct there is nothing to lens; remove the Lens derive entirely.
  4. Wrap the tuple struct in a named-field struct and derive Lens on the wrapper.

Example fix

// before
#[derive(Clone, Lens)]
struct Radius(f64);

// after
#[derive(Clone, Lens)]
struct Radius { value: f64 }
Defensive patterns

Strategy: validation

Validate before calling

// ensure the derive target has named fields:
// struct Good { field: T }   OK
// struct Bad(T);              tuple -> rejected
// struct Worse;               unit  -> rejected

Prevention

When it happens

Trigger: Applying `#[derive(Lens)]` to a tuple struct like `struct Point(f64, f64)` or a unit struct like `struct Marker;`. Raised in derive_struct called from derive_lens_impl.

Common situations: Deriving Lens on newtype wrappers (`struct Wrapper(inner: T)` written as tuple) or marker types; porting code where a struct was changed from named fields to a tuple struct after the derive was added.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10). Data as JSON: /api/errors/5ed6e694023b1da4. Report an issue: GitHub.

Appendix: source

Thrown at druid-derive/src/lens.rs:32

        Data::Struct(_) => derive_struct(&input),
        Data::Enum(e) => Err(syn::Error::new(
            e.enum_token.span(),
            "Lens implementations cannot be derived from enums",
        )),
        Data::Union(u) => Err(syn::Error::new(
            u.union_token.span(),
            "Lens implementations cannot be derived from unions",
        )),
    }
}

fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
    let ty = &input.ident;

    let fields = if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &input.data {
        Fields::<LensAttrs>::parse_ast(fields)?
    } else {
        return Err(syn::Error::new(
            input.span(),
            "Lens implementations can only be derived from structs with named fields",
        ));
    };

    if fields.kind != FieldKind::Named {
        return Err(syn::Error::new(
            input.span(),
            "Lens implementations can only be derived from structs with named fields",
        ));
    }

    let twizzled_name = if is_camel_case(&ty.to_string()) {
        let temp_name = format!("{}_derived_lenses", to_snake_case(&ty.to_string()));
        proc_macro2::Ident::new(&temp_name, proc_macro2::Span::call_site())
    } else {
        return Err(syn::Error::new(
            ty.span(),

View on GitHub (pinned to 0f8b1195e4)