Type-safe patterns for storing & serializing heterogenous state?

I'm making a card game in Rust. Each card in my game has different types of state associated with it. One card might need to store a chosen target, another might have counters, some cards have no state, etc. There's a pretty long list of possible pieces of state information a card might have. I'd like a way to handle this in a uniform way and serialize it when I write the game state to disk in a save file (using serde json):

#[derive(Serialize,Deserialize)
pub struct CardState {
  pub id: CardId,
  pub state: <Card State Goes Here>
}

I'd like to be able to store the state in the CardState struct, and maybe ideally have it be generic so operations are type safe (the appropriate state is guaranteed to exist for this card type at compile time). What are some good patterns for implementing the CardState?

Have you considered using a tagged union (enum where the variants contain data)?

#[derive(Serialize, Deserialize)]
pub enum Card {
    Attack(AttackCard),
    Defend(DefendCard),
}

The AttackCard and DefendCard types can contain anything you like. More on serializing and deserializing tagged unions Enum representations ยท Serde.