Структура Arc
#![allow(unused)] fn main() { pub struct Arc<T, A = Global> where A: Allocator, T: ?Sized, { /* приватные поля */ } }
Потокобезопасный (Send + Sync) счетчик ссылок с атомарными операциями.
Тип Arc<T> предоставляет совместное владение значением типа T, выделенным в куче. Вызов clone на Arc создает новый экземпляр Arc, который указывает на то же самое выделение в куче, увеличивая при этом счетчик ссылок. Когда последний указатель Arc на выделение выходит из области видимости, значение удаляется (деструктор запускается), и память освобождается.
Совместное владение в Rust по умолчанию осуществляется через проверку заимствований. Rc<T> и Arc<T> позволяют обойти это ограничение: Rc использует проверки во время выполнения для обеспечения целостности, Arc использует атомарные операции.
Совместное владение также означает неизменность: невозможно получить изменяемую ссылку (&mut T) на значение внутри Arc, если только не используется другой механизм для обеспечения мутабельности, например Mutex или RwLock.
Умные указатели
Значения типа Arc<T> являются умными указателями: они указывают на значение типа T, расположенное в куче, и имеют метаданные счетчика ссылок. При создании Arc с помощью Arc::new метаданные счетчика ссылок также сохраняются в куче рядом с T. При клонировании Arc создается новый умный указатель, который указывает на то же самое размещение в куче.
Когда Arc выходит из области видимости, его реализация Drop уменьшает счетчик ссылок. Если счетчик ссылок достигает нуля, значение внутри T удаляется, а память освобождается.
Примеры
Потокобезопастельное совместное использование изменяемых данных:
#![allow(unused)] fn main() { use std::sync::{Arc, Mutex}; use std::thread; let five = Arc::new(Mutex::new(5)); for _ in 0..10 { let five = Arc::clone(&five); thread::spawn(move || { let mut value = five.lock().unwrap(); *value += 1; }); } }
Clone
Создание ссылки на тот же самый allocation:
#![allow(unused)] fn main() { use std::sync::Arc; let foo = Arc::new(vec![1.0, 2.0, 3.0]); // Две ссылки на одни и те же данные let a = foo.clone(); let b = Arc::clone(&foo); // a, b и foo - все Arc, указывающие на одни и те же данные }
Функция Arc::clone(&from) семантически эквивалентна from.clone(), но более идиоматична.
Deref поведение
Arc<T> автоматически разыменовывается в T (благодаря реализации Deref), поэтому вы можете вызывать методы T непосредственно на Arc<T>. Чтобы избежать конфликта имен с методами T, методы Arc<T>которые являются ассоциированными функциями (вызываются как Arc::get_mut(&mut value) вместо value.get_mut()).
#![allow(unused)] fn main() { use std::sync::Arc; let my_arc = Arc::new(()); let my_arc_clone = Arc::clone(&my_arc); }
Также следует учитывать автоматическое разыменование при сравнении:
#![allow(unused)] fn main() { use std::sync::Arc; let five = Arc::new(5); assert!(five == 5); // Автоматическое разыменование в i32 }
Примеры совместного использования между потоками
#![allow(unused)] fn main() { use std::sync::Arc; use std::thread; let five = Arc::new(5); for _ in 0..10 { let five = Arc::clone(&five); thread::spawn(move || { println!("{five:?}"); }); } }
Примеры Weak
Слабые ссылки (Weak) не предотвращают удаление allocation:
#![allow(unused)] fn main() { use std::sync::{Arc, Weak}; let strong = Arc::new("hello".to_owned()); let weak = Arc::downgrade(&strong); // Сильная ссылка все еще существует, значение доступно assert_eq!(1, Arc::weak_count(&strong)); assert_eq!(Some("hello"), weak.upgrade().as_deref()); drop(strong); // Теперь значение удалено assert!(weak.upgrade().is_none()); }
Отличия от std::rc::Rc
Arc использует атомарные операции для подсчета ссылок, что означает его безопасность для совместного использования между потоками. Однако это влечет накладные расходы по производительности, которых нет у Rc. Rc не является потокобезопасным и не реализует Send или Sync.
Атомарность
Arc использует глобально атомарные операции для подсчета ссылок. На современных процессорах это часто достигается с помощью инструкций с блокировкой кэша. Однако системы без атомарных операций могут использовать спин-блокировки или другие механизмы.
Безопасность для циклических ссылок
Arc по умолчанию не предотвращает циклические ссылки. Это может привести к утечкам памяти. Для предотвращения циклических ссылок используйте Weak.
Трейт-объекты
Когда T имеет типаж Dyn (например, dyn SomeTrait), Arc автоматически реализует соответствующие типажи, такие как DynSomeTrait.
Реализации
Arc<str>:Arc<str>не имеет нуль-терминатора и может использоваться для строковых срезовArc<[T]>:Arc<[T]>может использоваться для срезов массиваArc<CStr>:Arc<CStr>может использоваться для строк в стиле C
Методы
Создание
new(value: T) -> Arc<T>- Создает новыйArc<T>new_cyclic(data_fn: F) -> Arc<T>- СоздаетArcс циклической ссылкойnew_uninit() -> Arc<MaybeUninit<T>>- Создает неинициализированныйArcpin(value: T) -> Pin<Arc<T>>- Закрепляет значение в памяти
Получение внутренних данных
into_raw(this: Arc<T>) -> *const T- Преобразует в сырой указательfrom_raw(ptr: *const T) -> Arc<T>- Восстанавливает из сырого указателяas_ptr(this: &Arc<T>) -> *const T- Получает сырой указатель
Счетчики ссылок
strong_count(this: &Arc<T>) -> usize- Количество сильных ссылокweak_count(this: &Arc<T>) -> usize- Количество слабых ссылок
Слабые ссылки
downgrade(this: &Arc<T>) -> Weak<T>- Создает слабую ссылкуtry_unwrap(this: Arc<T>) -> Result<T, Arc<T>>- Извлекает значение, если это последняя сильная ссылка
Сравнения
ptr_eq(this: &Arc<T>, other: &Arc<T>) -> bool- Проверяет, указывают ли на одно allocation
Реализации трейтов
Clone,Debug,Default,Deref,DropFrom<T>,From<Box<T>>,From<String>,From<&str>PartialEq,Eq,PartialOrd,Ord,HashSend,Sync(когдаT: Send + Sync)Pointer,Display
Автоматические реализации трейтов
!Unpin(когдаT: ?Unpin)UnwindSafe(когдаT: UnwindSafe)RefUnwindSafe(когдаT: RefUnwindSafe)