Search code examples
genericsrusttraits

Rust - clousures and generic types


To be honest, it's hard for me to describe the problem in words, so i'll show the code right away:

// SomeType / SomeTrait

struct SomeType;

trait SomeTrait {
    fn do_sth() -> &'static str;
}

impl SomeTrait for SomeType {
    fn do_sth() -> &'static str {
        "do_sth()"
    }
}

// SomeOtherType / SomeOtherTrait

struct SomeOtherType;

impl SomeOtherType {
    fn get_some_trait<C>(&self, c: C)
    where
        C: Fn(SomeType), // Takes clousure, clousure have to get `SomeType`-type paramm 
    {
        c(SomeType);
    }
}

trait SomeOtherTrait {
    fn perform_sth<C, D>(&self, c: C)
    where
        D: SomeTrait,
        C: Fn(&D) -> &'static str; // Takes clousure, clousure have to get type that implements SomeTrait
}

impl SomeOtherTrait for SomeOtherType {
    fn perform_sth<C, D>(&self, c: C)
    where
        D: SomeTrait,
        C: Fn(&D) -> &'static str,
    {
        self.get_some_trait(|arg: SomeType| {
            c(&arg); // <- Problem
            // Error: expected type parameter `D`, found struct `SomeType`
            // But `D: SomeTrait` and SomeType implements `SomeTrait`
        });
    }
}

fn main() {}

The code above is a simplified model of the situation I find myself in.

If i have |arg: SomeType|, the c clousure takes reference to generic type T, that implements SomeType - why can't i pass arg as argument for c?

Thank you in advance for your help in solving the problem. I apologise for any mistakes in my English.


Solution

  • I don't think your current definition of SomeOtherTrait allows this since it leaves filling in the D parameter to the caller by defining it on perform_sth but you internally already bind it to be SomeType by virtue of SomeOtherType::get_some_trait.

    If you introduce a generic parameter D on SomeOtherTrait, you can bind D in a given type's implementation of SomeOtherTrait to whatever it requires:

    trait SomeOtherTrait<D> {
        fn perform_sth<C>(&self, c: C)
        where
            D: SomeTrait,
            C: Fn(&D) -> &'static str; // Takes clousure, clousure have to get type that implements SomeTrait
    }
    
    impl SomeOtherTrait<SomeType> for SomeOtherType {
        fn perform_sth<C>(&self, c: C)
        where
            C: Fn(&SomeType) -> &'static str,
        {
            self.get_some_trait(|arg| {
                c(&arg);
            });
        }
    }
    

    The other option is adjusting get_some_trait to be generic over the argument to c, although you'll then need to be able to construct it, e.g. through D::Default():

    // ...
    struct SomeOtherType;
    
    impl SomeOtherType {
        fn get_some_trait<C, D>(&self, c: C)
        where
            D: Default,
            C: Fn(D), // Takes clousure, clousure have to get `SomeType`-type paramm
        {
            c(D::default());
        }
    }
    // ...
    

    which in turn entails adding the D: Default bound to SomeOtherTrait::perform_sth.