В C ++ есть три способа передачи параметров в функцию: по значению, по ссылке lvalue и по ссылке rvalue. Из них передача по значению создает владение в том смысле, что вызываемая функция получает свою собственную копию, а передача по ссылке rvalue указывает, что значение может быть использовано, то есть вызывающая сторона больше не будет использоваться. Передача по ссылке lvalue означает, что объект временно заимствован у вызывающей стороны.
Тем не менее, они, как правило, «по соглашению» и не всегда могут быть проверены компилятором. И вы можете случайно превратить ссылку lvalue в ссылку rvalue, используя std::move()
. Конкретно, есть три проблемы:
Ссылка может пережить объект, на который она ссылается. Пожизненная система Rust предотвращает это.
В любое время может быть активным более одной изменяемой / неконстантной ссылки. Руст заимствования чекер предотвращает это.
Вы не можете отказаться от ссылок. Вы не можете увидеть на сайте вызова, создает ли эта функция ссылку на ваш объект, не зная сигнатуру вызываемой функции. Поэтому вы не можете надежно предотвращать ссылки, не удаляя какие-либо специальные методы ваших классов, а также не проверяя сайт вызовов на соответствие некоторым руководствам по стилю «без ссылок».
Проблема на всю жизнь связана с базовой безопасностью памяти. Разумеется, использование ссылки после истечения срока действия ссылочного объекта запрещено. Но очень легко забыть о времени жизни, когда вы сохраняете ссылку в объекте, особенно когда этот объект переживает текущую область. Система типов C ++ не может объяснить это, потому что она вообще не моделирует время жизни объекта.
std::weak_ptr
Смарт - указатель делает собственность кодирования семантики , аналогичную простую ссылку, но требует, чтобы ссылка объект управляется через shared_ptr
, то есть является счетчик ссылок. Это не абстракция с нулевой стоимостью.
Хотя C ++ имеет систему const, она не отслеживает, может ли объект быть изменен, но отслеживает, можно ли изменить объект по этой конкретной ссылке . Это не дает достаточных гарантий для «бесстрашного параллелизма». Напротив, Rust гарантирует, что если есть активная изменяемая ссылка, которая является единственной ссылкой («Я единственный, кто может изменить этот объект»), и если есть не изменяемые ссылки, то все ссылки на объект не являются изменяемыми («Пока я могу читать с объекта, никто не может его изменить»).
В C ++ может возникнуть соблазн защитить доступ к объекту через интеллектуальный указатель с мьютексом. Но, как уже говорилось выше, если у нас есть ссылка, она может избежать ожидаемого срока службы. Поэтому такой умный указатель не может гарантировать, что он является единственной точкой доступа к своему управляемому объекту. Такая схема может на самом деле работать на практике, потому что большинство программистов не хотят саботировать себя, но с точки зрения системы типов это все еще совершенно несостоятельно.
Общая проблема со смарт-указателями заключается в том, что они представляют собой библиотеки поверх основного языка. Набор базовых языковых функций позволяет использовать эти умные указатели, например, std::unique_ptr
необходимо использовать конструкторы перемещения. Но они не могут исправить недостатки в базовом языке. Способности неявно создавать ссылки при вызове функции и иметь висячие ссылки вместе означают, что основной язык C ++ является нездоровым. Неспособность ограничить изменяемые ссылки одним-единственным означает, что C ++ не может гарантировать безопасность от условий гонки с любым видом параллелизма.
Конечно, во многих отношениях C ++ и Rust больше похожи, чем не похожи друг на друга, особенно в отношении их концепций статически определенных времен жизни объектов. Но хотя можно писать правильные программы на C ++ (при условии, что никто из программистов не допускает ошибок), Rust гарантирует правильность обсуждаемых свойств.