Подтвердить что ты не робот

Почему std:: rc:: Rc <> not Copy?

Может кто-нибудь объяснить мне, почему Rc<> не Copy?

Я пишу код, который использует много общих указателей, и набирать .clone() все время нервничает.

Мне кажется, что Rc<> должен состоять только из указателя, который является фиксированным размером, поэтому сам тип должен быть Sized и, следовательно, Copy, правильно?

Я что-то пропустил?

4b9b3361

Ответ 1

Мне кажется, что Rc<> должен состоять только из указателя, который имеет фиксированный размер, поэтому сам тип должен быть Sized и, следовательно, Copy, верно?

Это не совсем так. Rc является аббревиатурой правочника С ounted. Это означает, что тип отслеживает, сколько ссылок указывают на принадлежащие данные. Таким образом, мы можем иметь несколько владельцев одновременно и безопасно освободить данные, как только счетчик ссылок достигнет 0.


Но как сохранить счетчик ссылок действительным и актуальным? Точно, мы должны что-то делать, когда создается новая ссылка/владелец и всякий раз, когда ссылка/владелец удаляется. В частности, мы должны увеличить счетчик в первом случае и уменьшить его во втором.

Счетчик уменьшается путем реализации Drop, Rust-эквивалента деструктора. Эта функция drop() выполняется всякий раз, когда переменная выходит из области видимости - идеально подходит для нашей цели.

Но когда мы делаем приращение? Вы догадались: в clone(). Признак Copy по определению говорит, что тип можно дублировать, просто копируя биты:

Типы, которые могут быть скопированы простым копированием битов (например, memcpy).

В нашем случае это не так, потому что: да, мы "просто копируем биты", но мы также делаем дополнительную работу! Нам нужно увеличить наш счетчик ссылок!

Ответ 2

Тип не может реализовать Copy если он реализует Drop (источник). Поскольку Rc реализует его, чтобы уменьшить счетчик ссылок, это невозможно.

Кроме того, Rc не просто указатель. Он состоит из Shared:

pub struct Rc<T: ?Sized> {
    ptr: Shared<RcBox<T>>,
}

Который, в свою очередь, является не только указателем:

pub struct Shared<T: ?Sized> {
    pointer: NonZero<*const T>,
    _marker: PhantomData<T>,
}

PhantomData необходим для выражения права собственности на T:

этот маркер не имеет последствий для дисперсии, но необходим для dropck, чтобы понять, что мы логически владеем T

Для получения подробной информации см.: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data