Почему std :: rc :: Rc нуждается в PhantomData? - PullRequest
10 голосов
/ 20 сентября 2019

Я знаю, что PhantomData предназначен для использования параметра времени жизни или типа в определении типа данных, который в противном случае не использовался бы.Недавно я просматривал определение Rc в Rust std lib и заметил, что оно, похоже, использует PhantomData, но похоже, что T используется в поле одноуровневой ptrкак NonNull<RcBox<T>>.В документах говорится, что NonNull является "* mut T, но ненулевым и ковариантным." и далее расширяет это определение следующим утверждением:

В отличие от *mut T, NonNull<T> является ковариантным по отношению к T. Если это неверно для вашего варианта использования, вы должны включить некоторые PhantomData в ваш тип для обеспечения инвариантности, такие как PhantomData<Cell<T>> или PhantomData<&'a mut T>.

Следовательно, нужно ли это для дисперсии или больше, потому что NonNull фактически является необработанным указателем, а PhantomData требуется, чтобы потреблять исключенное время жизни, так как этот ответ, по-видимому, подразумевает ?

1 Ответ

5 голосов
/ 20 сентября 2019

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...