Search code examples
genericsrusttraitstype-bounds

How to add constraint that one generic type implements another generic type in Rust?


How can I make something like this work:

struct FooStruct<A, B> where A : B, B : ?Sized {...}

I searched for some type marker to tell the compiler that S must be a trait, searched the Rust documentation for some example of this pattern, and couldn't find other people having the same problem. Here is my code:

trait Factory<S> where S : ?Sized {
    fn create(&mut self) -> Rc<S>;
}

trait Singleton<T> {
    fn create() -> T;
}

struct SingletonFactory<T> {
    instance: Option<Rc<T>>
}

impl<S, T> Factory<S> for SingletonFactory<T> where S : ?Sized, T : S + Singleton<T> {
    fn create(&mut self) -> Rc<S> {
        if let Some(ref instance_rc) = self.instance {
            return instance_rc.clone();
        }
        let new_instance = Rc::new(T::create());
        self.instance = Some(new_instance.clone());
        new_instance
    }
}

The compiler fails with the following error :

      --> src/lib.rs:15:57
   |
15 | impl<S, T> Factory<S> for SingletonFactory<T> where T : S + Singleton<T> {
   |                                                         ^ not a trait

Solution

  • I managed to find an answer: the std::marker::Unsize<T> trait, although not a stable feature in the current version of Rust (1.14.0).

    pub trait Unsize<T> where T: ?Sized { }
    

    Types that can be "unsized" to a dynamically-sized type.

    This is broader than the "implements" semantic, but it is what I should have been searching for from the beginning, as the generic parameters in the example code can be other things than a struct and a trait or two traits (say sized and unsized arrays).

    The generic example in the question can be written:

    struct FooStruct<A, B>
        where A: Unsize<B>,
              B: ?Sized,
    {
        // ...
    }
    

    And my code :

    impl<S, T> Factory<S> for SingletonFactory<T>
        where S: ?Sized,
              T: Unsize<S> + Singleton<T>,
    {
        // ...
    }