PhantomData
используется здесь, чтобы сообщить средству проверки, что сброс Rc<T>
может привести к сбросу значения типа T
.
Когда мы сообщаем, что можемсбросив значение типа T
, средство проверки сброса гарантирует, что любые времена жизни в T
изживут сам struct
.Именно эта проверка предотвращает компиляцию следующего кода .В этом случае общим аргументом Rc
является PeekOnDrop<&'a u8>
, у которого есть время жизни 'a
.
use std::{fmt, rc::Rc};
struct PeekOnDrop<T: fmt::Debug>(T);
impl<T: fmt::Debug> Drop for PeekOnDrop<T> {
fn drop(&mut self) {
println!("{:?}", self.0);
}
}
struct SelfReferential<'a> {
value: Box<u8>,
rc: Option<Rc<PeekOnDrop<&'a u8>>>,
}
fn main() {
let mut sr = SelfReferential {
rc: None,
value: Box::new(1),
};
sr.rc = Some(Rc::new(PeekOnDrop(&*sr.value)));
// `sr` would be dropped here, which could drop `value` before `rc`.
// The destructor of `PeekOnDrop` would then try to inspect the (dangling)
// reference, resulting in UB!
}
Для полного объяснения базовой логики см. nomicon , но учтите, что без PeekOnDrop
предыдущий пример компилируется просто отлично .Это связано с тем, что Rc<T>
объявляет в Drop
impl , что его универсальный параметр T
равен #[may_dangle]
.При этом он обещает, что его Drop
impl ничего не делает со значением T
, которым он владеет, за исключением (возможно) его удаления.Ошибка возникает только в том случае, если средство проверки выпадения рекурсивно проверяет значение Drop
, равное PeekOnDrop
, и обнаруживает, что оно может получить доступ к T
.
Для полноты, приведен пример неопределенной программы , который заключается в утверждении, что PeekOnDrop
'Drop
impl не обращается к T
, используя #[may_dangle]
.Такое же неопределенное поведение было бы продемонстрировано в исходном примере, если бы Rc
не использовал PhantomData
, чтобы объявить, что оно может сбросить значение T
.