Sling Academy
Home/Rust/Creating Threads in Rust with std::thread::spawn

Creating Threads in Rust with std::thread::spawn

Last updated: January 06, 2025

Rust is known for its powerful concurrency model and provides several ways to create and manage threads. One of the simplest options available in the Rust standard library is the std::thread::spawn function. This function allows developers to spawn concurrent threads easily while ensuring safety and high performance. This article introduces how to create and manage threads using std::thread::spawn in Rust.

1. Understanding std::thread::spawn

The std::thread::spawn function is a part of Rust's standard library, and it is used to create a new thread of execution spawned from the main thread. When the main function starts, it initiates the main thread, which can then spawn additional threads as needed.

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("Thread created!");
    });

    handle.join().unwrap();
    println!("Main thread continues...");
}

In this simple example, we use thread::spawn to create a new thread. The join method is then called to block the main thread until the spawned thread finishes executing.

2. Capturing Environment Variables

Threads can also capture and access variables from their environment using closures. Here's an example:

use std::thread;

fn main() {
    let text_to_print = "Printing from the new thread!";

    let handle = thread::spawn(move || {
        println!("{}", text_to_print);
    });

    handle.join().unwrap();
}

Notice the use of the move keyword, which allows the spawned thread to take ownership of the variables used in its environment. This is essential to avoid data races or borrowing issues.

3. Handling Panics with Threads

Rust has strong memory safety guarantees, and threads can panic just like any part of an application. When a thread panics, the panic can be caught using Result.

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        panic!("Intentional Panic!");
    });

    let result = handle.join();

    match result {
        Ok(_) => println!("Thread finished without panic."),
        Err(e) => println!("Thread had an error: {:?}", e),
    }
}

The join method returns a Result which can be Ok if the thread completes successfully or Err if it panics, allowing for proper error handling.

4. Sharing Data Between Threads

Sharing data between multiple threads safely is a fundamental aspect of concurrent programming. In Rust, the safest way to share data across threads is by using Arc (Atomic Reference Counting).

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

fn main() {
    let data = Arc::new(Mutex::new(0));

    let mut handles = vec![];

    for _ in 0..10 {
        let data = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let mut data = data.lock().unwrap();
            *data += 1;
        });
        handles.push(handle);
    }

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

    println!("Data: {}", *data.lock().unwrap());
}

Here we use Arc to allow multiple ownership of the Mutex-protected data. Each thread safely increments the shared counter.

Conclusion

Using std::thread::spawn, you can easily create concurrent threads in Rust. Rust's compiler and borrowing rules ensure thread safety, protecting against many kinds of concurrency bugs at compile time. Whether you're handling data or managing ten threads or more, Rust's threading model lets you express logic simple and straightforward while leveraging modern hardware.

Next Article: Sync and Send Traits in Rust: Ensuring Safe Cross-Thread Data Access

Previous Article: Thread Safety in Rust: Harnessing the Ownership Model

Series: Concurrency in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior