Объекто безопасные трейты

58 views
Skip to first unread message

gig kaz

unread,
Apr 20, 2015, 4:36:14 PM4/20/15
to rust-r...@googlegroups.com
Доброго времени суток, Появилась необходимость создать HashMap, содержащий плагины (по моим прикидкам - это должны быть динамически диспетчеризуемые трейт объекты). Плагин - это просто структура, которая реализует трей Plugin.
let mut plugins : HashMap<& str, Box<Plugin>> = HashMap::new();
plugins.insert("template
", Box::new(template::Plugin::new()));
...
pub trait
Plugin {
    fn handle
<'a>(&'a Server<'a>, message: Message) -> Result<(), Error>;
}
...
mod template {
    pub struct Plugin;

    impl Plugin {
        pub fn new() -> Plugin {
            Plugin
        }
    }

    impl<'a> super::Plugin for Plugin {
        fn handle
<'a>(server: &'a Server<'a>, message: Message) -> Result<(), super::Error> {
            //...
            Ok(())
        }
    }
}

Ошибка говорит что трейт не является объектно безопасным:
src/main.rs:32:32: 32:65 error: cannot convert to a trait object because trait `test::plugin::Plugin` is not object-safe [E0038]
src/main.rs:32     plugins.insert("template", Box::new(template::Plugin::new()));
                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Что это за проблема такая и как сделать трейт безопасным?

PS: Если в метод трейта (handle) не передавать ссылку
на сервер, а использовать лишь семантику перемещения, то все работает.

Vladimir Matveev

unread,
Apr 20, 2015, 6:58:22 PM4/20/15
to gig kaz, rust-r...@googlegroups.com
Добрый день,

Здесь у вас есть две проблемы.

Во-первых, в вашем трейте объявлен только один статический метод.
Поэтому использование объектов здесь будет совершенно бесполезно -
статические методы (которые не принимают self ни в каком виде),
естественно, не попадают в таблицу виртуальных методов, и их нельзя
использовать через объекты - в самом деле, вы даже их вызвать не
сможете через объект, просто нет такого синтаксиса :)

Во-вторых, а вы точно описали все методы, которые содержатся в Plugin?
Дело в том, что не все трейты можно использовать для создания
трейт-объектов. Например, методы в трейте, которые принимают self по
значению или дженериковые методы заблокируют эту возможность. Код,
аналогичный вашему, у меня компилируется, но остаётся вероятность, что
я что-то делаю не то. Моё предположение, если трейт действительно
именно такой - мешает дженериковый lifetime-параметр у метода.
Попробуйте его убрать, тем более, что он здесь и не нужен - компилятор
сам способен вывести все lifetime'ы.

20 апреля 2015 г., 23:36 пользователь gig kaz <cht...@gmail.com> написал:
> --
> Вы получили это сообщение, поскольку подписаны на группу "Rust по-русски".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения,
> отправьте письмо на электронный адрес
> rust-russian...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке
> https://groups.google.com/d/msgid/rust-russian/89d7aeba-47c8-4598-8c4b-9459bdd7c730%40googlegroups.com.
> Чтобы настроить другие параметры, перейдите по ссылке
> https://groups.google.com/d/optout.

gig kaz

unread,
Apr 21, 2015, 4:14:47 AM4/21/15
to rust-r...@googlegroups.com, cht...@gmail.com
Спасибо за ответ.

1) Да, немного неправильно воспринимал статические методы трейта. Думал что для трейт объекта их можно вызвать и переопределить.
2) Я правильно понимаю, что следующий трейт можно использовать для создания трейт-объекта:
pub trait Plugin {
    fn handle
<'a>(&self, server: &'a Server<'a>, message: Message) -> Result<(), Error>;
}
так как &self передан по ссылке. Если же мы использовали бы self, то нельзя было бы использовать.? Насколько я понял - связано это с тем что размер ссылки известен на этапе компиляции, а самого self - нет. Но как дженериковые методы могут ограничить эту возможность? Ведь все дженерики на этапе компиляции резолвятся...?


вторник, 21 апреля 2015 г., 1:58:22 UTC+3 пользователь Владимир Матвеев написал:

Vladimir Matveev

unread,
Apr 21, 2015, 12:11:01 PM4/21/15
to gig kaz, rust-r...@googlegroups.com
Да, насчёт методов, принимающих self по значению, вы всё поняли правильно.

С дженериковыми методами проблема немного в другом. Дело в том, что
Rust применяет мономорфизацию дженериков - для каждого дженерикового
метода для каждого типа параметра, с которым он вызван, генерируется
отдельный код. В случае, когда метод резолвится статически, никаких
проблем нет. Но в случае динамической диспетчеризации вы не сможете
выяснить, какую именно специализацию дженерикового метода вызвать,
потому что для выбора метода на трейт-объекте используется таблица
виртуальных методов, и в случае дженериков в этой таблице должны были
бы содержаться ссылки на абсолютно все возможные специализации
дженериковых методов, для всех типов в принципе. Понятно, что это
невозможно.

21 апреля 2015 г., 11:14 пользователь gig kaz <cht...@gmail.com> написал:
> https://groups.google.com/d/msgid/rust-russian/5982016c-acb9-47c8-bb46-e6b5b8536d49%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages