dath.vg
dath.vg

Reputation: 104

Rust generic method in trait object

I'm trying to implement library that runs scripts written in various languages and extracts some callable object from them. Especially, I'm interested in function get_engine, which returns to you a factory by specifying file extension. Current implementation is:

#[cfg(test)]
mod tests;

use std::error::Error;

pub struct AutoLibrary<'a> {
    engines: Vec<
        Box<
            dyn AutomationFactory<
                'a,
                dyn AutomationScript<'a, dyn AutomationCommand<'a>>,
                dyn AutomationCommand<'a>,
                dyn Error,
            >,
        >,
    >,
}

impl<'a> AutoLibrary<'a> {
    fn get_engine(
        &self,
        name: impl AsRef<str>,
    ) -> Box<
        dyn AutomationFactory<
            'a,
            dyn AutomationScript<'a, dyn AutomationCommand<'a>>,
            dyn AutomationCommand<'a>,
            dyn Error,
        >,
    > {
        todo!()
    }
}

pub struct AssFile {/* doesn't matter for now */}

pub trait AutomationCommand<'a> {
    fn call(&self, file: AssFile) -> AssFile;
}

pub trait AutomationScript<'a, C>
where
    C: AutomationCommand<'a>,
{
    fn commands(&self) -> Vec<C>;
}

pub trait AutomationFactory<'a, S, C, E>
where
    C: AutomationCommand<'a>,
    S: AutomationScript<'a, C>,
    E: Error,
{
    fn load_script(&self, path: impl AsRef<str>) -> Result<Box<S>, E>;
}

Which doesn't compile for now. Compiler tries to say me that trait objects cannot contain generic methods, but there are no generic methods, only generic implementations. Also for that particular case I can't understand the reasoning. Compiler knows actual trait that object generic of and therefore can build and return a vtable, because the trait itself Always consumes the &self and in final implementation will always return specific objects.

Upvotes: 0

Views: 157

Answers (1)

PitaJ
PitaJ

Reputation: 15002

load_script is generic, due to impl AsRef<str>. impl Trait in parameter position is essentially just syntax sugar for a normal generic. Here's how load_script would desugar:

pub trait AutomationFactory<'a, S, C, E>
where
    C: AutomationCommand<'a>,
    S: AutomationScript<'a, C>,
    E: Error,
{
    fn load_script<R: AsRef<str>>(&self, path: R) -> Result<Box<S>, E>;
}

As you can see, the function is generic. To fix this, I'd recommend just changing it to accept and &str instead, and require conversion at the call site:

pub trait AutomationFactory<'a, S, C, E>
where
    C: AutomationCommand<'a>,
    S: AutomationScript<'a, C>,
    E: Error,
{
    fn load_script(&self, path: &str) -> Result<Box<S>, E>;
}

// somewhere else
let path = String::new();
factory.load_script(&path);

Upvotes: 2

Related Questions