# Arc в Rust: потокобезопасное разделённое владение

> Узнайте, как использовать Arc в Rust для безопасного разделения данных между потоками. Примеры с Arc, Rc, Mutex и практические советы.

- **URL:** https://eagle2000.space/rust/rust-arc/
- **Date:** 2025-06-05

- **Tags:** rust, dev

---## Введение

Если вы уже знакомы с `Rc<T>` (если не знакомы, то можете [прочитать прошлую статью](../rust-rc/)), то, возможно задавались вопросом - а можно ли безопасно делить данные между потоками? Ответ — можно, но для этого нужен другой тип: `Arc<T>`.

## Что такое Arc?

`Arc` расшифровывается как **Atomically Reference Counted** (атомарный счетчик ссылок). Он работает как `Rc`, но с одной важной особенностью: он потокобезопасен. То есть, в отличие от `Rc`, его можно использовать в многопоточном коде.

### Синтаксис базового использования:

```rust
use std::sync::Arc;

let data = Arc::new(vec![1, 2, 3]);
let data_cloned = Arc::clone(&data);
```

### Ключевые особенности Arc:
- Подсчёт ссылок происходит атомарно (через атомарные инструкции процессора).
- Может использоваться в нескольких потоках.
- Не даёт изменять данные напрямую — для этого используется `Mutex`, `RwLock` или другие инструменты о которых постараюсь рассказать в следующих статьях.

### Сравнение с Rc:

| Тип | Поддерживает многопоточность | Атомарный подсчет |
| --- | ---------------------------- | ----------------- |
| Rc  | ❌ Нет                        | ❌ Нет             |
| Arc | ✅ Да                         | ✅ Да              |


### Пример


### 💻 Пример проблемы с Rc в многопоточном коде


#### Шаг 1

```rust
// ?compile_fail
use std::rc::Rc;
use std::thread;

fn main() {
    let data = Rc::new(vec![1, 2, 3]);

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

    handle.join().unwrap();
}
```

**1. Этот код не скомпилируется.**
- Ошибка: `Rc<Vec<i32>> cannot be sent between threads safely`


#### Шаг 2

```rust
use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);
    let data_cloned = Arc::clone(&data);

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

    handle.join().unwrap();
}
```

**2. Теперь всё работает корректно.**


### Пример для случая когда нужно изменить данные


Arc сам по себе не даёт изменять содержимое. 

Если нужно менять данные из разных потоков, используйте `Mutex` (_мютекс это как замок, который позволяет работать с данными только одному потоку за раз_):




### 💻 Пример с изменением данных


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

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

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

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

    println!("Result: {}", *counter.lock().unwrap());
}
```

**Разберем подробнее что же тут происходит.**
_Это пример демонстрации безопасного изменения общего значения из нескольких потоков:_
1. `counter` — это общая переменная, защищённая `Mutex` и обёрнутая в `Arc` (как мы уже знаем он позволяет безопасно делиться данными между потоками).
2. В цикле создаётся 10 потоков с `thread::spawn` каждый из которых:
- получает свою копию `Arc` для `counter`
- блокирует `Mutex` для получения доступа к данным
- увеличивает значение на 1
3. `handle.join()` ждёт завершения всех потоков.




> Мы изучим `Mutex` и `thread::spawn` более глубоко в следующих статьях - это важные концепции языка Rust для работы с потоками.

## Практические советы по Arc

* Используйте `Arc` если данные нужны в нескольких потоках.
* Для изменяемых данных — комбинируйте с `Mutex`, `RwLock` и т.д.
* Не злоупотребляйте `Arc`: каждый клон — это атомарная операция, она дороже, чем обычный счётчик `Rc`.
* {{< keyword >}} 🦀 **hard skill не для начинающих!** {{< /keyword >}} По умолчанию `Arc<T>` — это `Send + Sync` для случая когда `T: Send + Sync`. 

## Заключение

Arc полезен когда вы работаете с многопоточностью. Он гарантирует безопасность при доступе к общим данным. Но требует осторожности в плане производительности. Если вам не нужен доступ из разных потоков - используйте `Rc`. Но когда дело доходит до многопоточного доступа — `Arc` становится незаменимым.

На сегодня это все!

{{< typeit
      speed=50
>}}
Продолжение следует...
{{< /typeit >}}

