Passing trait bounded structs to mpsc channels

    let character_timer = Box::new(move || character.update());
    let monster_timer = Box::new(move || monster.update());

    let (tx, rx) = mpsc::channel();

    tx.send(monster_timer as Box<dyn FnOnce()>).unwrap();
    tx.send(character_timer).unwrap();

A few things:

You don't want a concrete type, you want a trait object. And that trait has to be implemented by the types you are trying to erase (i.e. it has to be implemented by Timer, not by the type parameters to Timer.

Your Timer structure is, at least in this example, just a single-use callback. So I switched to using the existing trait for exactly this: FnOnce.

Everything here is single-shot because you've used mut self instead of &mut self for the update methods. If you changed to &mut self, you'd want to use FnMut instead.

If you want to add functionality to Timer itself and avoid having to manually specify closures, you'd need a trait implemented for Timer<T> (let's call it Update), and pass Box<dyn Update> to the channel. So something like:

use std::sync::mpsc;

trait Update {
    fn update(self);
}

struct Timer<T: Update> {
    state: T,
}

struct Character { hitpoints: u32 }

struct Monster { hitpoints: u32 }

impl Update for Character {
    fn update(mut self) {
        self.hitpoints += 10;
    }
}

impl Update for Monster {
    fn update(mut self) {
        self.hitpoints += 10;
    }
}

impl<T> Update for Timer<T> where T: Update {
    fn update(self) {
        self.state.update();
    }
}

fn main() {
    let character = Character { hitpoints: 100 };
    let monster = Monster { hitpoints: 200 };
    
    let character_timer = Timer {
        state: character,
    };

    let monster_timer = Timer {
        state: monster,
    };
    
    let (tx, _) = mpsc::channel();

    tx.send(Box::new(monster_timer) as Box<dyn Update>).unwrap();
    tx.send(Box::new(character_timer)).unwrap();
}

You would still want to change update to not use plain old self, though. As it stands, once you create the timer and put it in the channel, you can't ever look at the underlying state value ever again. Heck, once you call update, the state is gone, which makes the whole thing pretty useless. But how to address that depends heavily on what it is you're trying to do beyond a contrived example.