Fixed-size thread pool with std channels

Rust
use std::sync::{mpsc, Arc, Mutex};
use std::thread;

/// Run every job on one of the given number of worker threads, then join.
fn run_jobs<F>(jobs: Vec<F>, workers: usize)
where
    F: FnOnce() + Send + 'static,
{
    let (sender, receiver) = mpsc::channel::<F>();
    let receiver = Arc::new(Mutex::new(receiver));
    let handles: Vec<_> = (0..workers)
        .map(|_| {
            let receiver = Arc::clone(&receiver);
            thread::spawn(move || loop {
                let job = {
                    let guard = receiver.lock().expect("queue lock poisoned");
                    guard.recv()
                };
                match job {
                    Ok(job) => job(),
                    Err(_) => break,
                }
            })
        })
        .collect();
    for job in jobs {
        sender.send(job).expect("worker disappeared");
    }
    drop(sender); // closes the channel so every worker exits
    for handle in handles {
        handle.join().expect("worker panicked");
    }
}

fn main() {
    let jobs: Vec<_> = (0..8).map(|n| move || println!("job {n}")).collect();
    run_jobs(jobs, 4);
}