How to make generic protocol with associatedtype not generic via protocol inheritance?

285 views Asked by At

I have generic protocol with associated type and at some point would like to make it not generic. The same behaviour works fine with classes but unfortunately I can't use the same logic with protocols. For instance this is absolutely fine:

class Validator<Value> {
    func validate(_ value: Value?) -> String? {
        return nil
    }
}

class StingValidator: Validator<String> {
}

class EmptyStringValidator: StingValidator {
    override func validate(_ value: String?) -> String? {
        return ((value?.isEmpty ?? true) == true) ? "Empty" : nil
    }
}

func anyfunc() {
    let validator: StingValidator = EmptyStringValidator()
}

But using protocols I am getting compile error

Protocol 'StringValidatorProtocol' can only be used as a generic constraint because it has Self or associated type requirements

protocol ValidatorProtocol {
    associatedtype Value
    func validate(_ value: Value?) -> String?
}

protocol StringValidatorProtocol: ValidatorProtocol where Value == String {
}

struct EmptyStringValidator: StringValidatorProtocol {
    func validate(_ value: String?) -> String? {
        return ((value?.isEmpty ?? true) == true) ? "Empty" : nil
    }
}

func anyfunc() {
    let validator: StringValidatorProtocol = EmptyStringValidator()
}

Any ideas? Is there any way to make generic protocol not generic? I would appreciate any help!

2

There are 2 answers

0
AnderCover On

Making a protocol with associated type not generic is simply not possible and I think what you needed is more generics:

func anyfunc() {
    let validator: StringValidatorProtocol = EmptyStringValidator()
}

with a free function you can't do what you're trying to do, but inside a class or a struct:

protocol ValidatorProtocol {
    associatedtype Value
    init()
    func validate(_ value: Value?) -> String?
}

protocol StringValidatorProtocol: ValidatorProtocol where Value == String {
}

class EmptyStringValidator: StringValidatorProtocol {
    required init() {
        
    }
    func validate(_ value: String?) -> String? {
        return value?.isEmpty ?? true ? "Empty" : nil
    }
}

class MyClass<Validator: ValidatorProtocol> {
    static func validate(_ string: Validator.Value?) -> String? {
        let validator: Validator = Validator()
        return validator.validate(string)
    }
}
MyClass<EmptyStringValidator>.validate("Hello World") // nil
MyClass<EmptyStringValidator>.validate("") // "Empty"
MyClass<EmptyStringValidator>.validate(nil) // "Empty"

0
AudioBubble On

I don't know what "make it not generic" means, but let us know via an edit if this is not enough for you:

The error you've shown can be solved with an opaque constant.

let validator: some StringValidatorProtocol = EmptyStringValidator()