linebender/druid · error

Data implementations cannot be derived from unions

Error message

Data implementations cannot be derived from unions

What it means

The Data derive in druid-derive supports structs and enums only. Applying #[derive(Data)] to a union is rejected at compile time with this error, anchored at the union keyword's span, because the derive generates no code for unions.

Solutions

  1. Rewrite the union as an enum with variants for each field view, or as a struct, and derive Data on that.
  2. Remove #[derive(Data)] from the union and manage that data outside the druid data tree.
  3. Wrap the union in a struct that derives Data, implementing Data manually for the union-accessing parts (e.g. via same() comparison).

Example fix

// before
#[derive(Data, Clone, Copy)]
union Value { i: i32, f: f32 }
// after
#[derive(Data, Clone, Copy)]
enum Value { Int(i32), Float(f32) }
Defensive patterns

Strategy: validation

Validate before calling

// CI check: forbid Data derive on unions
// grep-based guard (run before cargo build):
// ! grep -rPzo '(?s)#[^
]*derive\s*\([^)]*\bData\b[^)]*\)(?s:.)*?\bunion\b' src/

Prevention

When it happens

Trigger: Writing #[derive(Data)] on a `union` definition, either directly or via a macro that blindly forwards derive attributes to all items in a module.

Common situations: FFI-style code with repr(C) unions where someone tries to make the union part of an app-data tree, macro-generated code deriving Data on every item.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at druid-derive/src/data.rs:17

// Copyright 2019 the Druid Authors
// SPDX-License-Identifier: Apache-2.0

//! The implementation for #[derive(Data)]

use crate::attr::{DataAttr, Field, FieldKind, Fields};

use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, Data, DataEnum, DataStruct};

pub(crate) fn derive_data_impl(
    input: syn::DeriveInput,
) -> Result<proc_macro2::TokenStream, syn::Error> {
    match &input.data {
        Data::Struct(s) => derive_struct(&input, s),
        Data::Enum(e) => derive_enum(&input, e),
        Data::Union(u) => Err(syn::Error::new(
            u.union_token.span(),
            "Data implementations cannot be derived from unions",
        )),
    }
}

fn derive_struct(
    input: &syn::DeriveInput,
    s: &DataStruct,
) -> Result<proc_macro2::TokenStream, syn::Error> {
    let ident = &input.ident;
    let impl_generics = generics_bounds(&input.generics);
    let (_, ty_generics, where_clause) = &input.generics.split_for_impl();

    let fields = Fields::<DataAttr>::parse_ast(&s.fields)?;

    let diff = if fields.len() > 0 {
        let same_fns = fields

View on GitHub (pinned to 0f8b1195e4)