Rust Lang

Rust Arc

Rust is a popular and compelling programming language with exceptional performance and safety. However, one of the most prominent features of Rust is its memory safety.

Unlike the low-level languages that deliver high performance but leave the memory safety on the user’s side, Rust ensures the memory safety by providing us with various features such as ownership and borrowing.

However, we may encounter such scenarios where we must share the data between multiple threads. Luckily, Rust provides us with a data type which is known as an Arc or Atomically Reference Counted which enables us to share the data across various threads but in a memory-safe way.

In this tutorial, we will explore the Arc type in Rust and see how to use it to share the data between multiple threads.

Rust Arc

A Rust Arc is simply a thread-safe reference-counting pointer. The Arc type provides a shared ownership of a value of a given type which is allocated in the heap memory.

Note: This type is only available on platforms that support the atomic loads and stores of pointers which includes all platforms that support the std crate but not all those which only support alloc.

Create an Arc in Rust

We must first create a value to create an Arc type in Rust. We then call the “Arc::new” function to the specified value. This should return an Arc instance which we can then use to share the fixed value across multiple threads.

An example is as follows:

use std::sync::Arc;
fn main() {
    let var = 42;
    let arc = Arc::new(var);
}

 
In the provided example, we use the “Arc::new” function to create a new type for the variable var. It is good to keep in mind that the function takes ownership of the specified value. This means that the value is moved into the Arc, and we cannot access it directly. Instead, we need to use the Arc instance to access its stored value.

Rust Access Value

We need to use the “Arc::clone” method on the instance to access a value that is stored in an Arc. The method creates a new Arc instance that shares the ownership of the underlying value.

This means that the original and the cloned Arc can access the value which allows us to share it across threads.

use std::sync::Arc;
fn main() {
    let var = 42;
    let arc = Arc::new(var);

    let clone = Arc::clone(&arc);

    assert_eq!(*arc, 42);
    assert_eq!(*clone, 42);
}

 
In this example, we create an Arc instance called “Arc” which holds the value of the var variable. We then use the “clone” method to create a new arc instance.

This allows us to use both the Arc and clone to access the value that is stored in the original Arc.

Note: We use the * operator to dereference the Arc and access the stored value. This is because the Arc itself is a smart pointer that provides a reference to the underlying value.

Share an Immutable Data in Arc

The following example shows how to use an Arc type to share an immutable data across threads:

use std::sync::Arc;
use std::thread;
fn main() {
    let value = 42;
    let arc = Arc::new(value);

    let thread1 = thread::spawn({
        let clone = Arc::clone(&arc);
        move || {
            // use clone to access the value
            println!("value: {}", clone)
        }
    });

    let thread2 = thread::spawn({
        let clone = Arc::clone(&arc);
        move || {
            println!("value: {}", clone)
        }
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}

 
In this example, we create an Arc instance which is “arc” with the value of 42. We then spawn two threads, passing a reference to Arc to each thread. Inside each thread, we call the “clone” method on the reference to create a new Arc instance which is “clone” that shares the ownership of the underlying value. Each thread can then use the clone to access the value that is stored in the Arc.

Note: We use the move keyword when creating the thread closures to move the ownership of the clone into the thread closure. This is necessary as the closures capture the environment by reference, and we need to ensure that each thread has its own unique reference to the Arc instance.

Conclusion

We explored how to create and use the Arc type in the Rust programming language which provides a safe and efficient method of sharing the data across threads. By using Arc, we can ensure that the ownership of the data that is shared between the threads is managed safely and that the concurrency access is thread-safe.

Despite its advantages, the Arc is not thread-safe by itself. Hence, the concurrent access to the same Arc instance can result in data races. Consider other synchronization mechanisms such as Mutex and RwLock for such tasks.

About the author

John Otieno

My name is John and am a fellow geek like you. I am passionate about all things computers from Hardware, Operating systems to Programming. My dream is to share my knowledge with the world and help out fellow geeks. Follow my content by subscribing to LinuxHint mailing list