Rust Guide > Documentation > Concurrency > JoinHandle

Introduction

In Rust, JoinHandle is a type that represents a handle to a thread. It is used to manage and control the lifecycle of a thread. A JoinHandle allows you to wait for a thread to finish executing and to retrieve its result. This is particularly useful in concurrent programming where you need to ensure that a thread completes its execution before proceeding with the rest of the program.

Using JoinHandle

To use JoinHandle, you need to spawn a thread using the std::thread::spawn function, which returns a JoinHandle. You can then call methods on the JoinHandle to interact with the thread, such as waiting for it to finish with the join method.

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("Hello from the spawned thread!");
    });

    handle.join().unwrap();
    println!("Hello from the main thread!");
}
// Output:
// Hello from the spawned thread!
// Hello from the main thread!

Key Methods

Below are some of the key methods exposed by the JoinHandle class:

join

Waits for the associated thread to finish and returns the result of the thread's execution.

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        42
    });

    let result = handle.join().unwrap();
    println!("Thread result: {}", result);
}
// Output:
// Thread result: 42

is_finished

Checks if the associated thread has finished execution.

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        thread::sleep(Duration::from_secs(2));
    });

    while !handle.is_finished() {
        println!("Thread is still running...");
        thread::sleep(Duration::from_millis(500));
    }

    println!("Thread has finished!");
}
// Output (approx):
// Thread is still running...
// Thread is still running...
// Thread is still running...
// Thread has finished!

Example Usage

Example 1: Basic Thread Spawning

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("Hello from the spawned thread!");
    });

    handle.join().unwrap();
    println!("Hello from the main thread!");
}
// Output:
// Hello from the spawned thread!
// Hello from the main thread!

Example 2: Returning a Value from a Thread

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        42
    });

    let result = handle.join().unwrap();
    println!("Thread result: {}", result);
}
// Output:
// Thread result: 42

Example 3: Checking if a Thread is Finished

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        thread::sleep(Duration::from_secs(2));
    });

    while !handle.is_finished() {
        println!("Thread is still running...");
        thread::sleep(Duration::from_millis(500));
    }

    println!("Thread has finished!");
}
// Output (approx):
// Thread is still running...
// Thread is still running...
// Thread is still running...
// Thread has finished!

Example 4: Handling Thread Panics

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        panic!("Something went wrong!");
    });

    match handle.join() {
        Ok(_) => println!("Thread completed successfully"),
        Err(e) => println!("Thread panicked: {:?}", e),
    }
}
// Output:
// Thread panicked: Any

Example 5: Using JoinHandle with Multiple Threads

use std::thread;

fn main() {
    let handles: Vec<_> = (0..5).map(|i| {
        thread::spawn(move || {
            println!("Thread {} is running", i);
        })
    }).collect();

    for handle in handles {
        handle.join().unwrap();
    }

    println!("All threads have finished");
}
// Output (order may vary):
// Thread 0 is running
// Thread 1 is running
// Thread 2 is running
// Thread 3 is running
// Thread 4 is running
// All threads have finished

Considerations

  • Always call join on a JoinHandle to ensure the thread completes. Failure to do so may result in resources not being properly cleaned up.
  • Use is_finished to check if a thread has completed without blocking the main thread.
  • Handle thread panics gracefully by matching on the result of join.

See Also

  • Mutex - A mutual exclusion primitive for protecting shared data.
  • RwLock - A read-write lock allowing multiple readers or a single writer.
  • Arc - An atomic reference-counted smart pointer for shared ownership across threads.