...делать какие-то вещи из привычной мне окружающей реальности.
Данный пост навеян двумя факторами:
- после поверхностного знакомства с Rust-ом при написании кода на C++ я зачастую задумываюсь о том, а как такие же вещи можно было бы сделать на Rust-е и насколько бы сложно было бы объяснить Rust-овому компилятору, что я сам понимаю, что делаю;
- прозвучавшей на днях в этих наших Интернетиках статьей в которой кто-то выступил от имени всего Microsoft-а: "Microsoft: Rust Is the Industry’s ‘Best Chance’ at Safe Systems Programming".
Так вот, в свете очередного обещания неизбежного пришествия Rust-а в индустрию с неизбежной заменой C++, мне захотелось узнать, а насколько идиоматично будет выглядеть на Rust-е решение, которое пару недель назад в текущем проекте довелось сделать на C++.
Вкратце суть такова: есть класс-родитель, который владеет неким объектом child. При этом у класса-родителя есть метод replace_child:
class child;
using child_unique_ptr = std::unique_ptr<child>;
class parent
{
child_unique_ptr m_child;
public:
void replace_child(child_unique_ptr new_child)
{
m_child = new_child;
}
...
};
|
Класс child -- это интерфейс, который имеет несколько реализаций. Что-то вроде:
class child
{
parent & m_parent;
public:
... // some pure virtual methods.
};
class first_stage : public child {...};
class second_stage : public child {...};
class third_stage : public child {...};
...
|
А фокус в том, что объекты-дети заменяют у родителя сами себя. Т.е. есть код вида:
class first_stage : public child
{
...
void on_some_event() override
{
...
m_parent.replace_child(std::make_unique<second_stage>(...));
}
};
|
Обратить внимание нужно на то, что только parent владеет указателем на child. И вызов replace_child синхронный. Поэтому, когда child вызывает replace_child у своего parent-а, то parent внутри replace_child-а уничтожает того child-а, который и сделал вызов replace_child.
Это ведет к тому, что значение this после возврата из replace_child будет невалидным. И, если в first_stage::on_some_event после возврата из replace_child будут происходить какие-то обращения к this (например, вызовы каких-то нестатических методов и/или чтение/изменение полей объекта), то возникнет UB.
В общем-то, примененное мной решение явно опасное и неоднозначное. Да и вообще программист из меня так себе, поэтому пишу код как умею. Отсюда и такие спорные решения.
Однако, если уж мне не удалось найти более безопасного решения в C++, то программируй я на Rust-е, то и на Rust-е бы я пришел бы к чему-либо подобному. И вот тут как раз и возникает вопрос, который мне любопытен:
А можно было бы в Rust-е применить такой же прием, не прибегая к unsafe?
Мои поверхностные знания Rust-е не позволяют дать точный ответ. Есть смутные подозрения, что без unsafe не обойтись.
Ну и добавлю сюда еще и то, что нормального ООП в Rust-е нет, поэтому иерархию из child-ов и их реализаций пришлось бы костылить без возможности просто-так переиспользовать функциональность, которая нужна всем (или значительной части) child-ов.