Pass constraint to protocol

So what I want to do is use the generic type of an interface to constrain another generic interface.

(pseudo code)

interface SomeInterface<T> {
    T someMethod();
}

interface SomeOtherInterface<T> {
    void someOtherMethod(SomeInterface<T> someImplementation);
}

SomeOtherInterface<SomeType> someOtherImplementation = new SomeOtherImplementation();

Now the call to someOtherMethod will constrain the parameter to the same generic type as the implementation.

Is this possible using protocols and if so...how?
I have tried a combination of associated types and generic method signatures but I don't quite get the same results.

You could translate the design to Swift like this:

protocol Factory {
    associatedtype Product
    func make() -> Product
}

protocol Consumer {
    associatedtype Product
    func consumeOutput<F: Factory>(of factory: F) where F.Product == Product
}

Then you can write types that conform to these protocols, like this:

struct Widget { }

struct WidgetFactory: Factory {
    func make() -> Widget {
        return .init()
    }
}

struct WidgetEater: Consumer {
    typealias Product = Widget
    func consumeOutput<F: Factory>(of factory: F) where F.Product == Product {
        let widget = factory.make()
        print("\(widget) tastes great")
    }

}

And you could use the conforming types like this:

let eater = WidgetEater()
eater.consumeOutput(of: WidgetFactory())
1 Like

Thanks, great answer!! BUT...
The problem is on the last line of my example. I can't figure out how to type hint for the protocol with an associated type. I don't want to limit my self to a specific implementation.

You can write type erasers. Typically you would name a type eraser by putting Any on the front of the protocol name. Since Factory has no generic methods, you can use a simpler technique for AnyFactory.

struct AnyFactory<Product>: Factory {
    init<F: Factory>(_ wrapped: F) where F.Product == Product {
        _make = wrapped.make
    }
    func make() -> Product { return _make() }
    private let _make: () -> Product
}

struct AnyConsumer<Product>: Consumer {
    init<C: Consumer>(_ wrapped: C) where C.Product == Product {
        box = Box(wrapped)
    }
    
    func consumeOutput<F>(of factory: F) where F : Factory, Product == F.Product {
        return box.consumeOutput(of: factory)
    }
    
    private let box: BoxBase
    
    private class BoxBase {
        func consumeOutput<F: Factory>(of factory: F) where F.Product == Product {
            fatalError()
        }
    }
    
    private class Box<Wrapped: Consumer>: BoxBase where Wrapped.Product == Product {
        init(_ wrapped: Wrapped) { self.wrapped = wrapped }
        let wrapped: Wrapped
        override func consumeOutput<F>(of factory: F) where Product == F.Product, F : Factory {
            return wrapped.consumeOutput(of: factory)
        }
    }
}
Terms of Service

Privacy Policy

Cookie Policy