How do we write type specific extensions to Array?

So I'd like to add an extension to Array but only when the Array is an array of a specific type. I tried starting with [Int].

In a playground:


extension Array where Generator.Element:Int {


mutating func addInts() {

self.append(1)

self.append(2)

}


}


var myIntArray = [Int]()

myIntArray.addInts()

Results in an error "[Int]" doesn't have a member named 'addInts'.

Any ideas? Is this even possible?

Constraining the generic type to a concrete type in a protocol extension seems to be impossible as of Xcode 7 beta 4. The same problem was reported in http://www.openradar.appspot.com/21512469. As a workaround, you could restrict the element type to a protocol instead:


extension Array where Element : IntegerType { ... }


(which is not the same of course).

this seems to work for me


//for [Int] arrays
extension SequenceType where Generator.Element == Int {   
    var average:Float {
        return Float(self.reduce(0) { $0 + $1 }) / Float(self.underestimateCount())
    }
}

Thanks but it doesn't seem to work for generic Array types though.

Thanks, that's dissapointing. I'll add a bug too.

I'll see if I can do what I need with a protocol.

Why are you trying to limit only to Arrays and not all SequenceTypes (Array, ContiguousArray, ArraySlice, Set, ...)?

Presumably because the CollectionType / MutableCollectionType / SequenceType protocols don't include the functionality to append additional elements, as shown in the op's example?


Maybe extending RangeReplaceableCollectionType would work?

Thanks for everybodys suggestions. I'm still plugging away on this. Yes, my requirements are array mutability and the ability to write type specific extensions.


Interestingly this works:


extension Array where Element : IntegerType {
    mutating func addItems() {
        self.append(1)
        self.append(2)
    }
}
var myIntArray = [Int]()
myIntArray.addItems()


However... I tried to do it with some simple types of my own and it doesn't...


protocol MyProtocol {}
struct MyContent : MyProtocol { let content = 1 }

extension Array where Element : MyProtocol {
    mutating func addItems() {
        self.append(MyContent())
        self.append(MyContent())
    }
}
var myArray = [MyProtocol]()
// myArray.append(MyContent())
myArray.addItems()


Error on self.append(): Can't invoke append with an argument list of (MyContent).


Any thoughts greatly appreciated.

This works:

extension Array where Element : MyProtocol {
    mutating func addItems() {
        self.append(MyContent() as! Element)
        self.append(MyContent() as! Element)
    }
}
var myArray = [MyContent]()
myArray.addItems()

In `extension Array where Element : MyProtocol`, Element may be any type conforming to MyProtocol, which can be an incompatible type with MyContent.


ADDITION:

Of course my code above would crash your app, if you made another type conforming to MyProtocol.

struct AnotherContent: MyProtocol { let content = "x" }
var anotherArray: [AnotherContent] = []
anotherArray.addItems() //-> Could not cast value of type 'MyContent' (0x10c5cf8d8) to 'AnotherContent' (0x10c5cf9e8).

When using this sort of workaround, you need to keep watching on MyContent being the only type conforming to MyProtocol.

my requirements are array mutability

If you can loosen your requirement a little bit, you can write something like this as Fault and LCS suggested.

//(Works in Xcode 7 beta 4 or later.)
extension RangeReplaceableCollectionType where Generator.Element == MyContent {
    mutating func addItems() {
        self.append(MyContent())
        self.append(MyContent())
    }
}
var myArray = [MyContent]()
myArray.addItems()


EDIT: was missing to note LCS's suggestion.

Thanks! This is actually a pretty good approach to the problem with one catch: You can't use a protocol as the element/array type or it will fail to compile.

I think I can live with that.


Overall it just smells like a workaround though and it definitely feels like I'm bumping up against beta compiler/standardlib quirks.


And I'm still intrigued that IntegerType works.


Thanks Fault, LCS, OOPer and all.

You can constrain it to your own protocol type, but if you want to append you (as noted by OOPer) have to create the right type to append. Unless your protocol includes an init or some other method that returns Self there is no way for you to do that.


IntegerType DOES have such a method: it inherits from IntegerLiteralConvertible, so the integer literals you were appending could be constructed as the correct type depending on whether, for example, the array was [Int] or [UInt8] or whatever.

Success! Somebody tag this as awesome. Thanks immaterial! Correct: the protocol needs init to be implemented.


protocol MyProtocol {
    init()
}
struct MyContent : MyProtocol {
    let content = 1

    init() {
    }
}
extension RangeReplaceableCollectionType where Generator.Element == MyProtocol {
    mutating func addItems() {
        self.append(MyContent())
        self.append(MyContent())
    }
}
var myArray = [MyProtocol]()
myArray.append(MyContent())
myArray.addItems()


I understand your explanation but not why it is true: The compiler already knows that MyContent conforms to MyProtocol and it already knows that MyContent can be constructed (a 'manual' init func isn't required for MyContent). That is all it should need. It looks like the compiler is loosing the ability to instantiate MyContent simply because it is inside an extension(?).


I'm missing some very basic understanding about how the type system is implemented.

Color be baffled - but happy!


Thanks.

Accepted Answer

Correct: the protocol needs init to be implemented.

Declaraing and implementing init() is not required for your code to work. You'll soon find this code works.

protocol MyProtocol {
}
struct MyContent : MyProtocol {
    let content = 1
}
extension RangeReplaceableCollectionType where Generator.Element == MyProtocol {
    mutating func addItems() {
        self.append(MyContent())
        self.append(MyContent())
    }
}
var myArray = [MyProtocol]()
myArray.append(MyContent())
myArray.addItems()


You'll have another chance to reconsider what is `the right type` in an extension.

Your right.


When I first tried extending "RangeReplaceableCollectionType" I used "Generator.Element : MyProtocol" not "Generator.Element == MyProtocol".


Sigh. Thanks all.

That's it. I believe immaterial tried to explain the `Element : MyProtocol` case.


With declaring and implementing init(), you can write something like this:

protocol MyProtocol {
    init()
}
struct MyContent : MyProtocol {
    let content = 1
}
extension Array where Element : MyProtocol {
    mutating func addItems() {
        self.append(Element())
        self.append(Element())
    }
}
var myArray = [MyContent]()
myArray.addItems()

MyContent() may not be of type Element, but Element() must be of type Element, that is the right type.

How do we write type specific extensions to Array?
 
 
Q