Каковы различия между необработанными указателями `* const T` и * mut T`? - PullRequest
8 голосов
/ 13 апреля 2019

Я пишу небезопасный код Rust, поэтому мне нужно знать точные различия между *const T и *mut T.Я предположил, что это похоже на &T и &mut T (то есть вы просто не можете мутировать T через &T, точка), но это не так!

Например, оболочка указателя NonNull<T> определяется следующим образом ( source ):

pub struct NonNull<T: ?Sized> {
    pointer: *const T,
}

Однако из этого можно получить *mut Tобертка через as_ptr, которая определяется как:

pub const fn as_ptr(self) -> *mut T {
    self.pointer as *mut T
}

Функция даже не помечена как unsafe!Мне не разрешено разыгрывать от &T до &mut T (по уважительной причине!), Но очевидно, что разыгрывать указатели, как это, хорошо.

Nomicon упоминает в главе о дисперсии , что *const T и *mut T отличаются дисперсией:

  • *const T: ковариант
  • *mut T: инвариант

Это единственная разница между типами указателей?Мне бы это показалось странным ...


В чем именно различия между типами указателей? Существуют ли ограничения для *const T, которых *mut T не имеет?Если различия минимальны: каковы дополнительные причины для включения обоих типов указателей в язык?

1 Ответ

2 голосов
/ 14 апреля 2019

Различия между *const T и *mut T

Основное различие между непостоянным и константным необработанным указателем заключается в том, что разыменование в них приводит к выражению непостоянного или неизменного места. Разыменование константного указателя дает неизменяемое место выражения , разыменование изменяемого указателя - изменяемый. Последствия изменчивости в соответствии со ссылкой на язык таковы:

Для выражения места, которое должно быть присвоено, заимствовано измененным образом, заимствовано неявно заимствовано или связано с шаблоном, содержащим ref mut, оно должно быть изменяемым.

Другое отличие между константными и изменяемыми указателями - это дисперсия типов, как вы уже заметили, и я думаю, что это все, что есть.

Приведение между изменяемыми и константными указателями

Вы можете привести *const T к *mut T в безопасном коде, поскольку различие в изменчивости становится значимым только после разыменования указателей, а разыменование необработанного указателя в любом случае является небезопасной операцией. Без приведения к изменяемому указателю вы не можете получить изменяемое выражение места для памяти, на которую указывает константный указатель.

Одна из причин, по которой Rust может быть немного более расслабленным в отношении изменчивости для необработанных указателей, заключается в том, что он не делает никаких предположений относительно псевдонимов для необработанных указателей, в отличие от ссылок. См. Какова семантика разыменования необработанных указателей? для получения дополнительной информации.

Почему NonNull использует *const T?

Тип указателя NonNull используется в качестве строительного блока для интеллектуальных указателей, таких как Box и Rc. Эти типы предоставляют интерфейсы, которые следуют обычным правилам Rust для ссылок - мутация pointee возможна только через владение или изменяемую ссылку на сам умный указатель, а общая ссылка на pointee может быть получена только заимствованием самого умного указателя , Это означает, что для этих типов безопасно быть ковариантным, что возможно, только если NonNull является ковариантным, что, в свою очередь, означает, что нам нужно использовать *const T вместо *mut T.

Почему язык включает два разных типа указателей, если они очень похожи?

Давайте подумаем об альтернативе. Если бы был только один тип указателя, он обязательно должен был бы быть изменяемым указателем - иначе мы не смогли бы изменить что-либо через необработанный указатель. Но этот тип указателя также должен быть ковариантным, поскольку в противном случае мы не смогли бы создавать ковариантные типы интеллектуальных указателей. (Всегда можно отказаться от ковариации, включив PhantomData<some invariant type> в структуру, но как только ваша структура станет инвариантной одним из ее членов, невозможно будет сделать ее снова ковариантной.) Поскольку изменяемые ссылки инвариантны, поведение этого воображаемого типа указателя было бы несколько удивительно.

Наличие двух разных типов указателей, с другой стороны, позволяет провести приятную аналогию со ссылками: указатели const ковариантны и обращаются к выражениям неизменного места, так же как общие ссылки, а изменяемые указатели являются инвариантными и обращаются к выражениям изменяемого места, просто как изменяемые ссылки.

Я могу только предположить, были ли это фактические причины для разработки языка, так как я не мог найти никакого обсуждения по этой теме, но решение не кажется мне необоснованным.

...