{"record":{"id":"18c51f72e3987968","repo":"linebender/druid","slug":"data-implementations-cannot-be-derived-from-unions","errorCode":null,"errorMessage":"Data implementations cannot be derived from unions","messagePattern":"Data implementations cannot be derived from unions","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"druid-derive/src/data.rs","lineNumber":17,"sourceCode":"// Copyright 2019 the Druid Authors\n// SPDX-License-Identifier: Apache-2.0\n\n//! The implementation for #[derive(Data)]\n\nuse crate::attr::{DataAttr, Field, FieldKind, Fields};\n\nuse quote::{quote, quote_spanned};\nuse syn::{spanned::Spanned, Data, DataEnum, DataStruct};\n\npub(crate) fn derive_data_impl(\n    input: syn::DeriveInput,\n) -> Result<proc_macro2::TokenStream, syn::Error> {\n    match &input.data {\n        Data::Struct(s) => derive_struct(&input, s),\n        Data::Enum(e) => derive_enum(&input, e),\n        Data::Union(u) => Err(syn::Error::new(\n            u.union_token.span(),\n            \"Data implementations cannot be derived from unions\",\n        )),\n    }\n}\n\nfn derive_struct(\n    input: &syn::DeriveInput,\n    s: &DataStruct,\n) -> Result<proc_macro2::TokenStream, syn::Error> {\n    let ident = &input.ident;\n    let impl_generics = generics_bounds(&input.generics);\n    let (_, ty_generics, where_clause) = &input.generics.split_for_impl();\n\n    let fields = Fields::<DataAttr>::parse_ast(&s.fields)?;\n\n    let diff = if fields.len() > 0 {\n        let same_fns = fields","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/linebender/druid/blob/0f8b1195e4e073f9597f2865299c3d18f8e4005f/druid-derive/src/data.rs#L1-L35","documentation":"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.","triggerScenarios":"Writing #[derive(Data)] on a `union` definition, either directly or via a macro that blindly forwards derive attributes to all items in a module.","commonSituations":"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.","solutions":["Rewrite the union as an enum with variants for each field view, or as a struct, and derive Data on that.","Remove #[derive(Data)] from the union and manage that data outside the druid data tree.","Wrap the union in a struct that derives Data, implementing Data manually for the union-accessing parts (e.g. via same() comparison)."],"exampleFix":"// before\n#[derive(Data, Clone, Copy)]\nunion Value { i: i32, f: f32 }\n// after\n#[derive(Data, Clone, Copy)]\nenum Value { Int(i32), Float(f32) }","handlingStrategy":"validation","validationCode":"// CI check: forbid Data derive on unions\n// grep-based guard (run before cargo build):\n// ! grep -rPzo '(?s)#[^\n]*derive\\s*\\([^)]*\\bData\\b[^)]*\\)(?s:.)*?\\bunion\\b' src/","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never add #[derive(Data)] to union definitions.","Use enums or structs for data-tree nodes reachable by druid.","If FFI unions are needed, wrap them in a struct with a manual (non-derive) Data implementation.","Run `cargo check` early; the proc-macro error names the union span directly."],"tags":["derive-macro","proc-macro","compile-time","unions"],"backgroundTag":"incompatible-source-type","analyzedSha":"0f8b1195e4e073f9597f2865299c3d18f8e4005f","analyzedAt":"2026-09-10T14:27:34.582Z","contentChangedAt":"2026-09-10T14:27:34.582Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}