But what is Arc in Rust and how to use it with rust traits

Using with traits in Rust is a bit more involved, but I'll do my best to explain it to you step by step.

dsysd dev
4 min readJul 27, 2023

Photo by Zdeněk Macháček on Unsplash

Understanding : What is it ?

stands for "Atomic Reference Counted" and is a type in Rust's standard library ().

It allows you to share ownership of data between multiple threads, ensuring that the data is deallocated only when the last reference to it is dropped.

This is useful when you need to pass data between threads or share it across different parts of your code safely.

Understanding Traits

In Rust, traits define behavior that types can implement.

Think of them as a set of functions that a type must provide to be considered as implementing that trait.

Traits enable you to write generic code that works with any type that implements the required behavior.

Now, let’s see how we can combine with traits.

Defining the Trait

First, you need to define the trait that describes the behavior you want your types to implement.

For example, let’s create a simple trait called that requires a function.

trait Printable {
fn print(&self);
}

Implementing the Trait for a Struct

Next, let’s create a simple struct and implement the trait for it.

struct MyData {
data: String,
}
impl Printable for MyData {
fn print(&self) {
println!("MyData: {}", self.data);
}
}

Using with the Trait

Now, we want to use to share the instance across different parts of our code.

use std::sync::Arc;

fn main() {
// Create an instance of MyData
let my_data = MyData {
data: "Hello, Arc!".to_string(),
};
// Create an Arc that holds a reference to the MyData instance
let arc_my_data: Arc<dyn Printable> = Arc::new(my_data);
// Clone the Arc so that we have another reference to the same data
let cloned_arc_my_data = Arc::clone(&arc_my_data);
// Now we can use both Arc references to the same data
arc_my_data.print();
cloned_arc_my_data.print();
}

In this example, we create an that holds a reference to the instance.

We use the function to create additional references to the same data. The function is called on both references, and it works as expected.

Understanding

You might have noticed that we used instead of .

This is because needs to know the size of the type it's holding at compile time, and trait objects like have a dynamic size that's not known at compile time.

To use with traits, we need to use the keyword to indicate that it's a trait object.

Using with traits allows us to store different types that implement the same trait within the same , enabling more flexibility and composability in our code.

That’s the basic explanation of using with traits in Rust. It provides you with a way to share trait objects across threads while ensuring proper memory management.

But What about Data mutation

You cannot directly mutate the data held by an between different threads.

The whole purpose of is to provide shared ownership of immutable data across multiple threads in a safe manner.

It enforces the rule that the data inside an cannot be mutated once it's shared.

When you have an , it allows multiple threads to have read-only access to the data simultaneously, but it does not provide a way for multiple threads to mutate the data concurrently.

If you need to mutate the data, you should use interior mutability patterns like or in combination with .

Using

If you want to mutate the data in a thread-safe manner, you can wrap the data inside a .

The enforces that only one thread can acquire the lock to the data at a time, ensuring exclusive access during the mutation.

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

struct MyData {
counter: Mutex<u32>,
}
impl MyData {
fn increment_counter(&self) {
let mut counter = self.counter.lock().unwrap();
*counter += 1;
}
}
fn main() {
let my_data = Arc::new(MyData {
counter: Mutex::new(0),
});
let threads: Vec<_> = (0..4)
.map(|_| {
let my_data_clone = Arc::clone(&my_data);
thread::spawn(move || {
my_data_clone.increment_counter();
})
})
.collect();
for t in threads {
t.join().unwrap();
}
let counter_value = *my_data.counter.lock().unwrap();
println!("Final Counter Value: {}", counter_value);
}

Using

If you need to allow multiple threads to read the data simultaneously but still need exclusive access for mutation, you can use (Read-Write Lock).

The important thing to remember is that when you use or with , you need to wrap the data in these types first before placing it inside the .

This ensures that thread-safe access and mutation are guaranteed.

If you need to mutate the data held by an from different threads, you should use interior mutability patterns like or in conjunction with to achieve thread safety.

Subscribe to my Youtube channel

Subscribe to my youtube channel if you are on the lookout for more such awesome content in video format.

https://www.youtube.com/@dsysd-dev

Claps Please!

If you found this article helpful I would appreciate some claps 👏👏👏👏, it motivates me to write more such useful articles in the future.

Follow for regular awesome content and insights.

Subscribe to my Newsletter

If you like my content, then consider subscribing to my free newsletter, to get exclusive, educational, technical, interesting and career related content directly delivered to your inbox

https://dsysd.beehiiv.com/subscribe

Important Links

Thanks for reading the post, be sure to follow the links below for even more awesome content in the future.

Twitter: https://twitter.com/dsysd_dev
Youtube: https://www.youtube.com/@dsysd-dev
Github: https://github.com/dsysd-dev
Medium: https://medium.com/@dsysd-dev
Email: dsysd.mail@gmail.com
Telegram 📚: https://t.me/dsysd_dev_channel
Linkedin: https://www.linkedin.com/in/dsysd-dev/
Newsletter: https://dsysd.beehiiv.com/subscribe
Gumroad: https://dsysd.gumroad.com/
Dev.to: https://dev.to/dsysd_dev/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

dsysd dev
dsysd dev

Written by dsysd dev

Helping you become an 11x developer. I write on distributed systems, system design, blockchain, and go. https://twitter.com/dsysd_dev

Responses (1)

Write a response

Sorry but the Arc<dyn Traits> part is not clear basically you're saying that Arc must know the size of the data at compile that's we use dyn Traits because it's size is unknown at compile time.. self contradicting. Please clarify this part, thanks. By the way MyData size is known at compile time no?