Swift Generic Constraints in Init

Swift Generic constraints in init

The problem is that the first init method

init<T: Equatable>(data: [T]) 

introduces a local type placeholder T which hides (and is completely
unrelated to) the placeholder T of the Generic type, so it
is essentially the same problem as in Array extension to remove object by value.

As of Swift 2 you can solve that with a "restricted extension":

extension Generic where T : Equatable {
init(data: [T]) {
let handler: (T, T) -> Bool = { $0 == $1 }
compare = handler
// ...
}
}

For Swift 1.x the only solution is probably to define a global helper
function

func makeGeneric<T : Equatable>(data: [T]) -> Generic<T> {
return Generic(compareHandler: { $0 == $1 }, data: data)
}

(and I could not think of a sensible name for the function :).

Swift - how to define a type constraint that guarantee the type has an init

You could construct a custom protocol that blueprints the init() method, and use this protocol as a type constraint for the generic typeholder in your createInstance function. E.g.:

protocol SimplyInitializable {
init()
}

struct MyStruct: SimplyInitializable {
var v: Int = 1
}

func createInstance<T: SimplyInitializable>(type: T.Type) -> T
{
return type.init()
}

let myType = MyStruct.self
let instance = createInstance(type: myType)

Type that (explicitly) conforms to SimplyInitializable above will be able to make use of the createInstance method (note the conformance of MyStruct to SimplyInitializable).


As an alternative to the approach above, you could likewise, if you'd like, make use of a generic struct rather than a global generic createInstance method to create instances of types that conform to SimplyInitializable. E.g.:

protocol SimplyInitializable {
init()
}

struct SimpleFactory<T: SimplyInitializable> {
static func createInstance() -> T {
return T.init()
}
}

struct MyStruct: SimplyInitializable {
var v: Int = 1
}

let instance = SimpleFactory<MyStruct>.createInstance()

How a generic class to conform a protocol (that have init in it) with constraint?

I don't know of a way to achieve this without making Graph final. If you do make it final, however, it will work fine as long as you remove required (required doesn't mean anything if there can't be subclasses):

extension Graph: ExpressibleByArrayLiteral where D == Int {
convenience init(arrayLiteral elements: Node...) {
self.init(Array(zip(0..., elements)))
}
}

swift convenience init and generic class

Generic placeholders are satisfied at the usage of the given generic type – therefore inside your convenience init, you cannot assume that T is a Section. It's an arbitrary concrete type that conforms to A.

For example, it would be perfectly legal for the caller to define a

struct SomeOtherSection : A {...}

and then call your convenience initialiser with T being SomeOtherSection.

The solution in this case is simple, you can just add your convenience initialiser in an extension of DataSource, where T is constrained to being Section – therefore allowing you to
call init(sections:) with a [Section]:

extension DataSource where T == Section {

convenience init() {
var section = Section()
section.items.append("Goodbye")
section.items.append("Swift")

self.init(sections: [section])
}
}

// ...

// compiler will infer that T == Section here.
let ds = DataSource()

Generic constraint

Let's start with the beginning, Validator<Value> is invalid syntax, even if Validator is a protocol with associated types. Swift doesn't yet allow existential containers (protocol references) to declare generic constraints, the exception being the where clause.

Thus, you'll need a type eraser to be able to use any kind of generics:

struct AnyValidator<Value>: Validator {
private let _validate: (Value) -> Bool

init<V: Validator>(_ validator: V) where V.Value == Value {
_validate = validator.validate
}

func validate(_ value: Value) -> Bool {
_validate(value)
}
}

extension Validator {
func eraseToAnyValidator() -> AnyValidator<Value> {
AnyValidator(self)
}
}

, and you'll need to update the definition as such:

struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]

init(_ validators: AnyValidator<Value>...) {
self.validators = validators
}

However the above implementation requires all callers to call .eraseToAnyValidator():

let compoundValidator = CompoundValidator(EmailValidator().eraseToAnyValidator(), RequiredValidator().eraseToAnyValidator())

If you want to decrease the burden on the caller, then you'll need do some work on the callee side:

struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]

init<V: Validator>(_ v: V) where V.Value == Value {
validators = [v.eraseToAnyValidator()]
}

init<V1: Validator, V2: Validator>(_ v1: V1, _ v2: V2) where V1.Value == Value, V2.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator()]
}

init<V1: Validator, V2: Validator, V3: Validator>(_ v1: V1, _ v2: V2, _ v3: V3) where V1.Value == Value, V2.Value == Value, V3.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator(), v3.eraseToAnyValidator()]
}

, basically you'll need to add as many initializers as many arguments you'll want to pass through the codebase. This enables you to call the initializer in the simple form:

let compoundValidator = CompoundValidator(EmailValidator(), RequiredValidator())

Can you restrict a Swift generic constraint to a set of known types?

Can I "or" the constraints somehow?

No, you cannot. However, this will accomplish the same objective.

Define a new protocol:

protocol MetadataRawValue { }

Add your conforming types:

extension Double: MetadataRawValue { }

extension Bool: MetadataRawValue { }

extension String: MetadataRawValue { }

And finally:

struct MetadataValue<T> where T: MetadataRawValue {
...
}

Swift: generic init inside a non generic struct

The T you want in this case is Never, since it can never have a value. To define that kind of init, you need to constrain it in an extension like this:

extension MyStruct where T == Never {
init(aBool: Bool, aInt: Int) {
self.init(aBool: aBool, aInt: aInt, aHashable: nil)
}
}

IMO, Swift should also allow this as:

init(aBool: Bool, aInt: Int) where T == Never {...}

But that's not currently legal Swift. You have to put it in an extension. It's just a syntax issue.

For completeness, here's the full code:

struct MyStruct<T> where T: Hashable {
let aBool: Bool
let aInt: Int
let aHashable: T?

init(aHashable: T?) {
self.init(aBool: false, aInt: 0, aHashable: aHashable)
}

private init(aBool: Bool, aInt: Int, aHashable: T?) {
self.aBool = aBool
self.aInt = aInt
self.aHashable = aHashable
}
}

extension MyStruct where T == Never {
init(aBool: Bool, aInt: Int) {
self.init(aBool: aBool, aInt: aInt, aHashable: nil)
}
}

let myStruct = MyStruct(aBool: true, aInt: 10)


Related Topics



Leave a reply



Submit