Либо Rc
, либо Arc
заменяет shared_ptr
. Что вы выберете, зависит от того, какой уровень потокобезопасности вам нужен для общих данных; Rc
— для случаев без потоков, а Arc
— когда вам нужны потоки:
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("ping"));
let b = a.clone();
println!("{{{}, {}}}", a, b);
}
Как и shared_ptr
, это не копирует сам String
. Он только увеличивает счетчик ссылок во время выполнения, когда вызывается clone
, и уменьшает счетчик, когда каждая копия выходит за пределы области действия.
В отличие от shared_ptr
, Rc
и Arc
имеют лучшую семантику потока. shared_ptr
является полупотокобезопасным. Счетчик ссылок shared_ptr
сам по себе потокобезопасен, но общие данные не "волшебным образом" сделаны потокобезопасными.
Если вы используете shared_ptr
в многопоточной программе, вам еще предстоит проделать большую работу, чтобы обеспечить ее безопасность. В программе без потоков вы платите за некоторую безопасность потоков, которая вам не нужна.
Если вы хотите разрешить изменение общего значения, вам также необходимо переключиться на проверку заимствования во время выполнения. Это обеспечивается такими типами, как Cell
, RefCell
, Mutex
и т. д. RefCell
подходит для String
и Rc
:
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let a = Rc::new(RefCell::new(String::from("ping")));
let b = a.clone();
println!("{{{}, {}}}", a.borrow(), b.borrow());
a.borrow_mut().push_str("pong");
println!("{{{}, {}}}", a.borrow(), b.borrow());
}
мы можем подсчитать количество ссылок на каждый объект во время компиляции и вызывать free только тогда, когда последняя ссылка выходит из области видимости.
Это почти то же самое, что Rust делает со ссылками. Он на самом деле не использует счетчик, а только позволяет вам использовать ссылки на значение, в то время как это значение гарантированно останется по тому же адресу памяти.
shared_ptr
в C++ не делает это во время компиляции. shared_ptr
, Rc
и Arc
— это конструкции времени выполнения, поддерживающие счетчик.
Можно ли сделать ссылку на объект, не аннулируя первую ссылку?
Это именно то, что Rust делает со ссылками, и то, что вы уже сделали:
fn main() {
let a = String::from("ping");
let b = &a;
println!("{{{}, {}}}", a, b);
}
Более того, компилятор запретит вам использовать b
, как только a
станет недействительным.
потому что переменные Rust копируются по ссылке, а не по значению
Это неправда. Когда вы присваиваете значение, право собственности на значение передается новой переменной. Семантически адрес памяти переменной изменился, и поэтому чтение этого адреса может привести к небезопасности памяти.
мы должны принять привычку работать только со ссылкой
Да, использование ссылок, когда это возможно, является наиболее идиоматичным выбором. Они требуют нулевых накладных расходов во время выполнения, и компилятор сообщит вам об ошибках, а не обнаружит их во время выполнения.
Конечно, бывают случаи, когда Rc
или Arc
полезны. Часто они нужны для циклических структур данных. Вы не должны расстраиваться из-за их использования, если вы не можете получить простые ссылки для работы.
со ссылкой на ссылку?
Это немного обратная сторона, так как дополнительная косвенность вызывает сожаление. Если вам действительно нужно, вы можете уменьшить его. Если вам не нужно изменять строку, вы можете вместо этого переключиться на Rc<str>
:
use std::rc::Rc;
fn main() {
let a: Rc<str> = Rc::from("ping");
let b = a.clone();
println!("{{{}, {}}}", a, b);
}
Если вам нужно сохранить возможность иногда изменять String
, вы также можете явно преобразовать &Rc<T>
в &T
:
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("ping"));
let b = a.clone();
let a_s: &str = &*a;
let b_s: &str = &*b;
println!("{{{}, {}}}", a_s, b_s);
}
Смотрите также:
person
Shepmaster
schedule
14.04.2018