2

I have been struggling to implement an extension for Swift Array to remove an element by value. There are proposed solutions that use down casting, which I am trying to avoid due to the cost associated with dynamic type checking. Would you be able to point out how to get the following code to compile.

Thanks,


protocol Removable: SequenceType {
    typealias Element
    mutating func remove(v: Element) -> Bool
}

func removeElementByValue<T: Equatable>(inout array: [T], valueToRemove: T) -> Bool {
    for (index, value) in enumerate(array) {
        if value == valueToRemove {
            array.removeAtIndex(index)
            return true
        }
    }
    return false
}

extension Array: Removable {
    mutating func remove(v: T) -> Bool {
        return removeElementByValue(&self, v) // compile error here
    }

// tried also with:
// mutating func remove<E:Equatable where E == T>(v: T) -> Bool 
}
1
  • Thanks @milos for your response. What I really was hoping to achieve was to get this to compile. I could have moved the global function inside the extension, applied cast at equality check and would have worked. Here is the concern, assuming both the protocol and function where given how do I get the above to compile. Hence my conclusion that obviously besides that I lack the knowledge to reconcile across protocols, generics, and extensions may be language needs t have a bit more on specifying the where clauses. Commented Oct 4, 2014 at 18:51

2 Answers 2

3

The reason people use down-casting ... along the lines of:

unsafeBitCast(self, [X].self) // where self is the array and `X` is the type of the passed in value

... is because trying to restrict Array's Element type in any way is tantamount to:

extension Array<T: Equatable> {} // --> Compiler error: Extension of generic type 'Array' cannot add requirements

A way to achieve an analogous functionality (without resorting to global functions) is to defer the comparison to the calling code by which time Element's type is resolved and known to be either Equatable or not:

var array = [1,2,3,4,5]
let filtered = array.filterOut { $0 == 3 }
filtered // --> [1,2,4,5]

... or, closer to what you are trying to achieve:

let didRemove = array.remove { $0 == 3 }
didRemove // --> true
array // --> [1,2,4,5]

... which could be implemented, for example, as follows:

extension Array {

    func filterOut(predicate: T -> Bool) -> [T] {
        return self.filter { !predicate($0) }
    }

    mutating func remove(predicate: T -> Bool) -> Bool {
        let count = self.count
        self = self.filterOut(predicate)
        return count != self.count
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

I don't think you can achieve exactly what you want. After playing around with Swift for a while, I've come to the conclusion that as of this writing the "natural" way to do things in Swift is with a combination of extensions and global functions. You'll notice that this is exactly how Swift works out of the box. For instance, Array lacks a contains method, but there is a global contains function that works with any SequenceType. Why is it done this way? Because of the limitations of Swift's type system. Array<T> can contain any type, but a proper implementation of contains requires an Equatable. The only way to constrain T to be Equatable is to use a global function, and this is what Swift does. Also, if you want to write truly generic functions on sequences, not just on Array<T>, you should be writing global functions that take SequenceType as a parameter, especially since it is not possible to write extension methods on SequenceType, so its functions must be global. (Though there are ways around this, e.g., by creating a protocol that extends SequenceType, but you'd have to declare all the types that support that extended protocol. Not my first choice.)

So, I just wouldn't do what you're trying to do. It feels unnatural to me in Swift, even if Swift's way of doing things feels unnatural to someone coming from, say, C# or Java. However, if pressed, I think Milos has come up with as good a solution as you're going to get.

Hopefully a future iteration of Swift will allow us to create extension methods with generic type constraints. Until that day, I will use global functions over SequenceType rather than over [T].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.