Rc
или Arc
Когда вам нужно долевое владение , Rc
или Arc
, обычно это первый инструмент, к которому обращаются.Эти типы реализуют совместное использование путем подсчета ссылок, поэтому клонирование одного обходится дешево (просто скопируйте указатель и увеличьте счетчик ссылок).В этом случае любой из них работает удобно:
struct Sphere {
radius: f64,
material: Rc<dyn Material>,
}
let m1 = Rc::new(Iron {});
let s1 = Sphere {
radius: 1.0,
material: m1,
};
m1
относится к конкретному типу Rc<Iron>
, но поскольку он реализует черту CoerceUnsized
, он может быть автоматически вызывается в контекстах, ожидающих Rc<dyn Material>
.Вы можете сделать несколько Sphere
ссылок на один и тот же материал, набрав clone
ing m1
.( Полный пример )
Разница между Rc
и Arc
заключается в том, что Arc
безопасно использовать для совместного использования между несколькими потоками, а Rc
- нет.(Также см. Когда использовать Rc vs Box? )
Ссылки
Что касается вашего справочного примера:
Это также работает, нонасколько я понимаю, мои материалы будут размещаться в стеке, а не в куче.
Это правда, что времена жизни получены из стека, но сама ссылка не делаетнужно указать на что-то в стеке.Например, вы можете взять ссылку на T
в Box<T>
, разыменовав Box
:
struct Sphere<'a> {
radius: f64,
material: &'a dyn Material,
}
let m1 = Box::new(Iron {});
let s1 = Sphere {
radius: 1.0,
material: &*m1, // dereference the `Box` and get a reference to the inside
};
let s2 = Sphere {
radius: 2.0,
material: &*m1,
};
Это даже дешевле, чем использовать Rc
, поскольку &
ссылки Copy
, но даже если сам Iron
хранится в куче, ссылки, указывающие на него, все еще связаны с временем жизни стек переменной m1
.Если вы не можете заставить m1
жить достаточно долго, вы, вероятно, захотите использовать Rc
вместо простых ссылок.
Ссылка на Box
Этот подход также долженработать, хотя это и не нужно.Причина этого заключается в том, что, хотя вы можете привести Box<Iron>
к Box<dyn Material>
, вы не можете привести &Box<Iron>
к &Box<dyn Material>
;типы несовместимы.Вместо этого вам нужно создать переменную стека типа Box<dyn Material>
, чтобы вы могли ссылаться на нее.
let m1: Box<dyn Material> = Box::new(Iron {}); // coercion happens here
let s1 = Sphere {
radius: 1.0,
material: &m1, // so that this reference is the right type
};