У меня есть черта, которую можно назвать T. Во многих местах я сейчас использую Rc<dyn T>.
Проблема в том, что я хотел добавить в T метод по умолчанию, который запускает нужную функцию Rc<dyn T>. Но, насколько я вижу, нет возможности применить Rc<Self>, потому что Self не имеет размера в черте. Я не могу заставить весь проект использовать дженерики, как мне это обойти?
пример:
use std::rc::Rc;
trait T {
fn run_test_method(self: Rc<Self>) {
test_method(self);
}
}
struct A;
impl T for A {}
fn test_method(_rc: Rc<dyn T>) {
// do stuff with 'rc'
}
fn main() {
let a = Rc::new(A) as Rc<dyn T>;
a.run_test_method();
}

Компилятор дает вам несколько подсказок по этому поводу, и если вы последуете им всем, вы получите что-то вроде этого, что компилируется:
trait T {
fn run_test_method(self: Rc<Self>)
where
Self: Sized + 'static,
{
test_method(self);
}
}
Граница Sized необходима для того, чтобы можно было выполнить приведение, а граница 'static необходима, поскольку в аргументе dyn T нет времени жизни test_method, что неявно делает ее dyn T + 'static.
Если по какой-то причине вы не можете потребовать время жизни 'static (поскольку разработчики T содержат нестатические заимствования), то вы можете удалить привязку 'static к T::run_test_method, если укажете, что dyn T может иметь нестатическое время жизни test_method:
fn test_method(_rc: Rc<dyn T + '_>) {
// do stuff with 'rc'
}
Если вы хотите иметь возможность вызывать этот метод с помощью Rc<A> или Rc<dyn T>, вам придется реализовать это отдельно для обоих случаев, потому что по сути каждый метод делает что-то свое. Один приводит значение размера (указатель на a) к объекту типажа, а другой — нет.
Вы можете добиться этого, используя вспомогательную черту, которую вы реализуете для обоих случаев. (Названия типов здесь могут немного сбить с толку людей, которые регулярно работают с Rust, потому что T обычно является общим типом, но здесь это особенность.)
// Remove the method from this type.
trait T {}
trait RunTestMethod {
fn run_test_method(self);
}
impl<X: T> RunTestMethod for Rc<X> {
fn run_test_method(self) {
test_method(self);
}
}
impl RunTestMethod for Rc<dyn T + '_> {
fn run_test_method(self) {
test_method(self);
}
}
Обратите внимание, что компилятор допускает эти реализации, поскольку они не перекрываются. Обобщенные типы неявно являются Sized, если не указано иное, а dyn T является !Sized — поэтому компилятор может легко доказать, что наборы типов, которые будет охватывать каждая реализация, не пересекаются.
Теперь вы можете сделать это:
fn main() {
let a = Rc::new(A);
Rc::clone(&a).run_test_method();
let b: Rc<dyn T> = a;
b.run_test_method();
}
@jk_games Я обновил свой ответ, добавив раздел, объясняющий, как этого можно добиться в Rust. В конечном счете, все это своего рода хак, и вполне вероятно, что вам следует подумать о перепроектировании своей особенности, хотя я не могу с легкостью сказать вам, как это сделать, не зная, что она делает и как она взаимодействует с остальной частью системы.
Я отредактирую свой вопрос, но метод run_test_method должен быть доступен для вызова из
Rc<dyn T>