Roman
Roman

Reputation: 1500

Extend Collection: Array and Set, but not Dictionary

extension Array {
    init?<S: Sequence>(_ sequence: S?) where S.Element == Element {
        guard let sequence = sequence else { return nil }
        self.init(sequence)
    }
}

extension Set {
    init?<S: Sequence>(_ sequence: S?) where S.Element == Element {
        guard let sequence = sequence else { return nil }
        self.init(sequence)
    }
}

As you can see the code in both extensions is absolutely identical. So how can I rewrite it without repetitiveness? Something like:

extension Collection where type == Array || type == Set {
    init?<S: Sequence>(_ sequence: S?) where S.Element == Element {
        guard let sequence = sequence else { return nil }
        self.init(sequence)
    }
}

Upvotes: 0

Views: 69

Answers (2)

Sweeper
Sweeper

Reputation: 273380

The initialiser you want to add to Arrays and Sets can in fact be added to all Sequences, because you are not using anything that is unique in Array or Set. All that the initialiser depends on is:

  • having an Element associated type
  • having an initialiser that takes a Sequence

Sequence satisfies the first criteria, and there is no common protocol between Array and Set that satisfies the second :-(. The one from Array comes from RangeReplaceableCollection. The one from Set comes from SetAlgebra.

You can create such a protocol yourself though:

protocol SequenceInitialisable : Sequence {
    init<S: Sequence>(_ s: S) where S.Element == Element
}

extension Array : SequenceInitialisable {}
extension Set : SequenceInitialisable {}

extension SequenceInitialisable {
    init?<S: Sequence>(_ sequence: S?) where S.Element == Element {
        guard let sequence = sequence else { return nil }
        self.init(sequence)
    }
}

Or (as you said) you can just do sequence.map(Array.init) if you don't mind writing map and init.


A tip on writing extensions: Try to find all the things that your extension depends on/needs, and then find the most general type that has all of those things. That is the type you should write the extension on.

Upvotes: 3

Rob C
Rob C

Reputation: 5073

You can do something like this:

protocol NilSequenceInitializable {}

extension NilSequenceInitializable where Self: Sequence {
    init?<S: Sequence>(_ sequence: S?) where S.Element == Element {
        guard let sequence = sequence else { return nil }
        self.init(sequence)
    }
}

extension Array: NilSequenceInitializable {}
extension Set: NilSequenceInitializable {}

Upvotes: 1

Related Questions