Понижение типа с именованным параметром времени жизни

У меня есть следующий код:

use std::any::Any;
use std::borrow::Cow;

// Base trait for downcasting
pub trait AsTranslationUnit<'a> {
    fn as_any(&self) -> &dyn Any;
}

pub trait TranslationUnit<'a>: AsTranslationUnit<'a> {
    fn file_stem(&self) -> &Cow<'a, str>;
}

// Implementation of AsTranslationUnit for all types implementing TranslationUnit
impl<'a, T: TranslationUnit<'a> + 'a> AsTranslationUnit<'a> for T {
    fn as_any(&self) -> &dyn Any {
        self
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct ModuleInterfaceModel<'a> {
    pub file_stem: Cow<'a, str>,
}

impl<'a> TranslationUnit<'a> for ModuleInterfaceModel<'a> {
    fn file_stem(&self) -> &Cow<'a, str> {
        &self.file_stem
    }
}

fn main() {
    let model = ModuleInterfaceModel {
        file_stem: Cow::Borrowed("example"),
    };

    let tu: &dyn TranslationUnit = &model;

    if let Some(mi) = tu.as_any().downcast_ref::<ModuleInterfaceModel>() {
        println!("Module Interface: {:?}", mi);
    } else {
        println!("Not a Module Interface");
    }
}

Что приводит к следующей ошибке:

   Compiling playground v0.0.1 (/playground)
error[E0310]: the parameter type `T` may not live long enough
  --> src/main.rs:16:9
   |
16 |         self
   |         ^^^^
   |         |
   |         the parameter type `T` must be valid for the static lifetime...
   |         ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
14 | impl<'a, T: TranslationUnit<'a> + 'a + 'static> AsTranslationUnit<'a> for T {
   |                                      +++++++++

For more information about this error, try `rustc --explain E0310`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Я хотел бы привести любой тип TranslationUnit к исходному типу, но все они содержат именованные параметры времени жизни, поэтому я не могу успешно применить этот подход.

Каким был бы более идиоматический подход к решению этой проблемы без дополнительных границ времени жизни 'static (что, очевидно, не решает мою проблему) и без выхода из именованного параметра времени жизни в моих структурах?

Вы могли бы добавить метод fn as_module_interface_model(&self) -> Option<&ModuleInterfaceModel> к своей черте TranslationUnit?

eggyal 03.07.2024 20:32

@eggyal Я могу. Вы имеете в виду понижение его в реализациях признака для каждого реализатора? Или что-то подобное?

Alex Vergara 04.07.2024 09:05

Черта может содержать реализацию функции по умолчанию, которая возвращает None, а реализация ModuleInterfaceModel может переопределить ее, просто вернув Some(self). Тогда вам вообще не нужно переходить через Any: можно прямо вниз с &dyn TranslationUnit.

eggyal 04.07.2024 09:34
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
3
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Any не поддерживает время жизни. Период.

Есть несколько ящиков, поддерживающих пожизненный Any, например временный, но я не могу ручаться за их надежность, так как не проверял их.

Вот пример его использования:

use std::borrow::Cow;

use transient::{Any, Downcast, Inv, Transient};

// Base trait for downcasting
pub trait AsTranslationUnit<'a> {
    fn as_any(&self) -> &dyn Any<Inv<'a>>;
}

pub trait TranslationUnit<'a>: AsTranslationUnit<'a> + Any<Inv<'a>> {
    fn file_stem(&self) -> &Cow<'a, str>;
}

// Implementation of AsTranslationUnit for all types implementing TranslationUnit
impl<'a, T: TranslationUnit<'a> + 'a> AsTranslationUnit<'a> for T {
    fn as_any(&self) -> &dyn Any<Inv<'a>> {
        self
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Default, Transient)]
pub struct ModuleInterfaceModel<'a> {
    pub file_stem: Cow<'a, str>,
}

impl<'a> TranslationUnit<'a> for ModuleInterfaceModel<'a> {
    fn file_stem(&self) -> &Cow<'a, str> {
        &self.file_stem
    }
}

fn main() {
    let model = ModuleInterfaceModel {
        file_stem: Cow::Borrowed("example"),
    };

    let tu: &dyn TranslationUnit = &model;

    if let Some(mi) = tu.as_any().downcast_ref::<ModuleInterfaceModel>() {
        println!("Module Interface: {:?}", mi);
    } else {
        println!("Not a Module Interface");
    }
}

Хм... почти. Но я столкнулся с этой ошибкой после получения Transient для ModuleInterfaceModel<'a>: черта Static не реализована для project_model::modules::ModuleInterfaceModel<'a>

Alex Vergara 04.07.2024 09:04

@AlexVergara Этот код компилируется, я проверил. Если у вас возникли проблемы с выводом, убедитесь, что все поля также выводятся Transient.

Chayim Friedman 04.07.2024 12:42

ваш код компилируется и работает, как описано. Проблема заключалась в том, что в реальном коде у меня есть вложенные структуры, которые теперь также являются временными, но они должны быть Clone, что отсутствует в описании ошибки, и я этого не заметил.

Alex Vergara 04.07.2024 12:59

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

Как создать самореферентную структуру?
Почему T должен быть статическим при клонировании в Rc<RefCell<dyn Trait>>?
Rust HashMap поддерживает стабильность указателя, позволяя ключу ссылаться на его значение
Сериализация/десериализация Rust JSON с помощью serde/serde_json при использовании дженериков и времени жизни?
Почему в Rust дженерикам необходимо указывать время жизни как статическое при передаче в качестве аргумента?
Понимание времени жизни Rust в смешанных изменяемых и неизменяемых ссылках
Отношения родитель/потомок в Rust без загрязнения на всю жизнь
Присвоение параметра метода RefCell локальной переменной приводит к ошибке компиляции
Как вернуть срез массива, созданного внутри функции (в чистом виде Rust)
Явное время жизни для себя в признаке, похоже, приводит к тому, что «E0499 не может заимствовать `*emitter` как изменяемый более одного раза» в цикле