Можно ли достичь модели владения Rust с помощью универсальной оболочки C ++?

15

Просматривая эту статью о безопасности параллелизма Rust:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

Мне было интересно, сколько из этих идей может быть реализовано в C ++ 11 (или новее). В частности, я могу создать класс владельца, который передает владение любому методу, которому он может быть передан? Кажется, что в C ++ есть так много способов передачи переменных, что это было бы невозможно, но, возможно, я мог бы наложить некоторые ограничения на класс или шаблон, чтобы гарантировать выполнение некоторого кода шаблона при каждом проходе метода?

Brannon
источник
Некоторые цитаты из ссылки могли бы улучшить этот вопрос
Мартин Ба
2
@delnan (Safe) Rust гарантирует, что у вас никогда не будет более одной изменяемой ссылки на что-то одновременно, и что у вас никогда не будет изменяемой ссылки на то, на что у вас также есть ссылки только для чтения. Он также имеет некоторые ограничения на передачу данных между потоками. Вместе они предотвращают значительный класс связанных с многопоточностью ошибок и упрощают анализ состояния объектов даже в однопоточном коде.
CodesInChaos
3
Вы не думаете, что можете выразить заимствование способом, который мог бы проверить компилятор C ++, поэтому вам придется прибегнуть к принудительному исполнению во время выполнения с соответствующим падением производительности.
CodesInChaos
1
Разве владения областью уже не реализованы умными указателями в C ++ 11?
Акшат Махаджан
1
@JerryJeremiah Rust имеет широкий спектр типов ссылок. Основные из них &не требуют какого-либо продвижения. Если вы попытаетесь получить какое- &mutто время еще одну ссылку (изменяемую или нет) на тот же элемент, вы не сможете скомпилировать. RefCell<T>перемещает проверку во время выполнения, поэтому вы получите панику, если попытаетесь .borrow_mut()что-то, что уже имеет активную .borrow()или .borrow_mut(). Rust также имеет Rc<T>(указатель общего владения) и его родного брата Weak<T>, но это касается владения, а не изменчивости. Вставьте RefCell<T>их внутрь для изменчивости.
8bittree

Ответы:

8

В 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 гарантирует правильность обсуждаемых свойств.

Амон
источник
Если проблема в том, что C ++ не отслеживает владение базовым языком, возможно ли реализовать эту функцию с помощью метапрограммирования? Это означает, что вы создадите новый класс интеллектуальных указателей, который будет безопасен для памяти, (1) заставляя его указывать исключительно на объекты, использующие только интеллектуальные указатели из того же класса, и (2) отслеживая владение с помощью шаблонов
Эллиот Гороховский,
2
@ElliotGorokhovsky Нет, потому что шаблон не может отключить основные функции языка, такие как ссылки. Умный указатель может затруднить получение ссылки, но в этот момент вы боретесь с языком - большинству стандартных библиотечных функций нужны ссылки. Также невозможно проверить время жизни ссылки с помощью шаблонов, потому что язык не предлагает точную концепцию времени жизни.
Амон
Эллиот Гороховский