5
func getIndex<T: Equatable>(valueToFind: T) -> Int? {...}

mutating func replaceObjectWithObject<T: Equatable>(obj1: T, obj2: T) {
    if let index = self.getIndex(obj1) {
        self.removeAtIndex(index)
        self.insert(obj2, atIndex: index)           // Error here: 'T' is not convertible to 'T'
    }
}

I have that function which is suppose to replace an element with another element. But Im not very familiar with Generics and don't know why this is not working. Please help.

If I remove the Equatable from the mutating func the error message jumps to the first line in that func and if I then replace that with the func find() that gives me the same error as on line 3.

3
  • Can you provide the implementation of the insert method, or at least its signature? Commented Sep 2, 2014 at 19:15
  • nvm... I figure that out - it's an Array extension, isn't it? Commented Sep 2, 2014 at 19:16
  • Slightly tangential, but that error message is confusing because Array uses T as its generic subtype. It's saying that T (the generic type for this function) is not convertible to T (the generic type for the array). If you change the method signature to have U instead of T it will be more clear. Commented Sep 2, 2014 at 19:34

5 Answers 5

4

This is actually not possible via an extension under the existing system of protocols and generics in Swift - you can't add additional constraints to the generic subtype of a type, so you can't extend Array with a method that requires that its contents conform to Equatable.

You can see this restriction in action with the built-in Array type -- there's no myArray.find(element) method, but there is a global function find() that takes a collection and an element, with a generic constraint that the collection's elements are Equatable:

func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C, value: C.Generator.Element) -> C.Index?

You can do this for your method - you just need to write a similar top-level function:

func replaceObjectWithObject<C : RangeReplaceableCollectionType where C.Generator.Element : Equatable>(inout collection: C, obj1: C.Generator.Element, obj2: C.Generator.Element) {
    if let index = find(collection, obj1) {
        removeAtIndex(&collection, index)
        insert(&collection, obj2, atIndex: index)
    }
}

var myArray = [1, 2, 3, 4, 5]
replaceObjectWithObject(&myArray, 2, 7)
// 1, 2, 7, 4, 5
Sign up to request clarification or add additional context in comments.

4 Comments

Use of undeclared type RangeReplaceableCollectionType, 'Generator' is not a member of type 'C'
Which beta are you using? RangeReplaceableCollectionType came in beta 6.
Hehe... Im using beta 4
Thanks for the explanation. I hope we get a way to add extra type constraints on member methods in Swift (e.g., like in Scala). Mixing top-level functions and member function calls in a single expression makes it harder to read (and harder to write).
0

How did you declare your Array extension? The problem is that your generic functions require arguments of type Equatable, but when you declared the array, you specified a specific implementation of an Equatable class, like String. A T is not a String without a cast.

Comments

0

What you are trying to do cannot be done using class/struct functions - @Nate Cook has provided a very good solution using a global function.

By the way the reason why it doesn't work becomes clearer if in your extension methods you replace T with V: they are different types. That also explains why the same error occurs if you remove the dependency from Equatable: the array holds objects of T type, but you are trying to insert a V value.

Comments

0

This answer is for a duplicate question stated her: Create swift array extension for typed arrays

There is a way to solve extensions to Array that are only applicable to a specific type of array. But you have to use an Array with elements of type Any, which sort of circumvents Swift's type system. But the code still works even if there are elements of other types in the array. See example below.

class Job {
    var name: String
    var id: Int
    var completed: Bool

    init(name: String, id: Int, completed: Bool) {
        self.name = name
        self.id = id
        self.completed = completed
    }
}

var jobs: [Any] = [
    Job(name: "Carpenter", id: 32, completed: true),
    Job(name: "Engineer", id: 123, completed: false),
    Job(name: "Pilot", id: 332, completed: true)]



extension Array {

    // These methods are intended for arrays that contain instances of Job

    func withId(id: Int) -> Job? {
        for j in self {
            if  (j as? Job)?.id == id {
                return j as? Job
            }
        }
        return nil
    }

    func thatAreCompleted() -> [Job] {
        let completedJobs =  self.filter { ($0 as? Job) != nil && ($0 as? Job)!.completed}
        return completedJobs.map { $0 as! Job }
    }
}

jobs.withId(332)
println(jobs.withId(332)?.name)
//prints "Optional("Pilot")"

let completedJobs = jobs.thatAreCompleted().map {$0.name}
println(completedJobs)
//prints "[Carpenter, Pilot]"

Comments

0

You can use extension with a where clause constraint and I'm using Xcode 7.3.1

extension Array where Element: Equatable {
    func testEqutability() {
        let e1 = self[0]
        let e2 = self[1]
        if e1 == e2 {//now we can use == to test Element equtability
            //do something 
        }
    }
}

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.