linera-io/linera-protocol · error · syn::Error

#[derive(StableEnum)] does not yet support generic enums

Error message

#[derive(StableEnum)] does not yet support generic enums

What it means

`#[derive(StableEnum)]` generates serde Serialize/Deserialize impls keyed by per-variant tags computed from variant names; generic enums are explicitly unsupported because the tag scheme has no representation for type parameters. `reject_generics` fails compilation for any enum with type/lifetime parameters or a where clause.

Solutions

  1. Split the generic enum into concrete enums (one per payload type) and derive each.
  2. Hoist the varying payload out of the enum: keep a non-generic StableEnum for the discriminant and store the payload alongside it in a struct.
  3. If generics are essential, implement Serialize/Deserialize manually instead of using the derive.
  4. For lifetime-only parameters, switch to owned data (`String`, `Vec<u8>`) so the enum is non-generic.

Example fix

// before
#[derive(StableEnum)]
enum Message<T> { Set(T), Clear } // does not yet support generic enums

// after
#[derive(StableEnum)]
enum MessageKind { Set, Clear }
struct Message<T> { kind: MessageKind, value: Option<T> }
Defensive patterns

Strategy: validation

Validate before calling

// compile-time guard: StableEnum requires a non-generic enum
// `cargo check` fails fast with 'does not yet support generic enums' — keep enums concrete

Prevention

When it happens

Trigger: Deriving `StableEnum` on `enum E<T> { A(T), B }`, on an enum with a lifetime parameter, or on an enum carrying a `where` clause.

Common situations: Porting a generic state-machine enum to Linera's stable serialization; sharing one generic enum between an application contract and its Rust host/SDK code.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/12e19f2ba7bf202d. Report an issue: GitHub.

Appendix: source

Thrown at linera-sdk-derive/src/stable_enum.rs:89

            .iter()
            .find(|(_, t, _): &&(String, u32, &Variant)| *t == tag)
        {
            return Err(Error::new(
                variant.span(),
                format!(
                    "Keccak-256 tag collision between variants `{other_name}` and `{name}` \
                     (tag = {tag:#010x}). Rename one of the variants."
                ),
            ));
        }
        out.push((name, tag, variant));
    }
    Ok(out)
}

fn reject_generics(input: &ItemEnum) -> Result<()> {
    if !input.generics.params.is_empty() || input.generics.where_clause.is_some() {
        return Err(Error::new(
            input.generics.span(),
            "#[derive(StableEnum)] does not yet support generic enums",
        ));
    }
    Ok(())
}

/// Emits the combined `Serialize` + `Deserialize` + `StableEnumTrace` impls.
pub fn generate_all(input: &ItemEnum, crate_root: CrateRoot) -> Result<TokenStream2> {
    let ser = generate_serialize(input, &crate_root)?;
    let de = generate_deserialize(input, &crate_root)?;
    let tr = generate_trace(input, &crate_root)?;
    Ok(quote! {
        #ser
        #de
        #tr
    })
}

View on GitHub (pinned to 6c226ddcb3)