Различия между *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 ковариантны и обращаются к выражениям неизменного места, так же как общие ссылки, а изменяемые указатели являются инвариантными и обращаются к выражениям изменяемого места, просто как изменяемые ссылки.
Я могу только предположить, были ли это фактические причины для разработки языка, так как я не мог найти никакого обсуждения по этой теме, но решение не кажется мне необоснованным.