ios swift generics protocols type-alias

ios - ¿Cómo agrego diferentes tipos que se ajustan a un protocolo con un tipo asociado a una colección?



swift generics (1)

Los protocolos con alias de tipo no se pueden usar de esta manera. Swift no tiene una manera de hablar directamente sobre metatipos como ValidationRule o Array . Solo puede tratar instancias como ValidationRule where... o Array<String> . Con los typealiases, no hay forma de llegar allí directamente. Entonces, tenemos que llegar allí indirectamente con borrado de tipo.

Swift tiene varios tipos de borradores. AnySequence , AnyGenerator , AnyForwardIndex , etc. Estas son versiones genéricas de protocolos. Podemos construir nuestra propia AnyValidationRule :

struct AnyValidationRule<InputType>: ValidationRule { private let validator: (InputType) -> Bool init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) { validator = base.validate } func validate(input: InputType) -> Bool { return validator(input) } }

La magia profunda aquí es el validator . Es posible que haya otra forma de borrar el tipo sin un cierre, pero esa es la mejor manera que conozco. (También odio el hecho de que Swift no puede manejar la validate como una propiedad de cierre. En Swift, los captadores de propiedades no son métodos adecuados. Por lo tanto, necesita la capa adicional de validator indirecta).

Con eso en su lugar, puede hacer los tipos de matrices que desea:

let len = ValidationRuleLength() len.validate("stuff") let cond = ValidationRuleCondition<String>() cond.validate("otherstuff") let rules = [AnyValidationRule(len), AnyValidationRule(cond)] let passed = rules.reduce(true) { $0 && $1.validate("combined") }

Tenga en cuenta que la eliminación de tipo no descarta la seguridad de tipo. Simplemente "borra" una capa de detalles de implementación. AnyValidationRule<String> sigue siendo diferente de AnyValidationRule<Int> , por lo que esto fallará:

let len = ValidationRuleLength() let condInt = ValidationRuleCondition<Int>() let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)] // error: type of expression is ambiguous without more context

Como ejercicio de aprendizaje, estoy reescribiendo mi biblioteca de validación en Swift.

Tengo un protocolo ValidationRule que define cómo deberían verse las reglas individuales:

protocol ValidationRule { typealias InputType func validateInput(input: InputType) -> Bool //... }

El tipo asociado InputType define el tipo de entrada que se validará (por ejemplo, String). Puede ser explícito o genérico.

Aquí hay dos reglas:

struct ValidationRuleLength: ValidationRule { typealias InputType = String //... } struct ValidationRuleCondition<T>: ValidationRule { typealias InputType = T // ... }

En otro lugar, tengo una función que valida una entrada con una colección de ValidationRule s:

static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult { let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage } return errors.isEmpty ? .Valid : .Invalid(errors) }

Pensé que esto iba a funcionar, pero el compilador no está de acuerdo.

En el siguiente ejemplo, aunque la entrada es una Cadena, el rule1 de la InputType es una Cadena, y el rule2 la InputType es una Cadena ...

func testThatItCanEvaluateMultipleRules() { let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 } let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2") let invalid = Validator.validate(input: "", rules: [rule1, rule2]) XCTAssertEqual(invalid, .Invalid(["message1", "message2"])) }

... recibo un mensaje de error extremadamente útil:

_ no es convertible a ValidationRuleLength

que es críptico pero sugiere que los tipos deberían ser exactamente iguales?

Entonces mi pregunta es ... ¿cómo agrego diferentes tipos que se ajustan a un protocolo con un tipo asociado en una colección?

¿No estás seguro de cómo lograr lo que intento o si es posible?

EDITAR

Aquí está sin contexto:

protocol Foo { typealias FooType func doSomething(thing: FooType) } class Bar<T>: Foo { typealias FooType = T func doSomething(thing: T) { print(thing) } } class Baz: Foo { typealias FooType = String func doSomething(thing: String) { print(thing) } } func doSomethingWithFoos<F: Foo>(thing: [F]) { print(thing) } let bar = Bar<String>() let baz = Baz() let foos: [Foo] = [bar, baz] doSomethingWithFoos(foos)

Aquí tenemos:

Protocol Foo solo se puede usar como una restricción genérica porque tiene requisitos de tipo Self o asociados.

Entiendo que. Lo que necesito decir es algo como:

doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) { }