Приведение Rc<Self> признака T к Rc<T> в ржавчине

У меня есть черта, которую можно назвать 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();
}
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Компилятор дает вам несколько подсказок по этому поводу, и если вы последуете им всем, вы получите что-то вроде этого, что компилируется:

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();
}

(Детская площадка)

Я отредактирую свой вопрос, но метод run_test_method должен быть доступен для вызова из Rc<dyn T>

jk_games 19.03.2024 22:01

@jk_games Я обновил свой ответ, добавив раздел, объясняющий, как этого можно добиться в Rust. В конечном счете, все это своего рода хак, и вполне вероятно, что вам следует подумать о перепроектировании своей особенности, хотя я не могу с легкостью сказать вам, как это сделать, не зная, что она делает и как она взаимодействует с остальной частью системы.

cdhowie 19.03.2024 22:11

Другие вопросы по теме