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.

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)

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.