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
Avoid Automatic Framework Linking in Swift
Swift: Differencebetween a Typealias and an Associatedtype with a Value in a Protocol
Migration from Swift 3 to Swift 4 - Cannot Convert String to Expected String.Element
Drag and Drop Image from Uicollectionview to Uiview
Swift Repl: How to Save/Load the Repl State? (A.K.A. Suspend/Resume, Snapshot, Clone)
Timer Not Firing Every Second on Watchkit
Swiftui View and Uihostingcontroller in Uiscrollview Breaks Scrolling
Swift Tdd & Async Urlsession - How to Test
What Is Darwinboolean Type in Swift
Behaviour of Protocols with Self
Generic Method Override Not Working in Swift
Swift Use Unicode Character in Localization.Strings
Swift 4.1 Deinitialize and Deallocate(Capacity:) Deprecated
Argument of '#Selector' Does Not Refer to an '@Objc' Method, Property or Initializer