Kyle Decot
Kyle Decot

Reputation: 20815

Cannot invoke 'contains' with an argument list of Type '([Foo], Foo)'

I'm attempting to use the contains function in swift to see if my objects is in a typed array but I'm getting:

Cannot invoke 'contains' with an argument list of Type '([Foo], Foo)'

class Foo {

}

let foo = Foo()
let foos = [Foo(), Foo()]

contains(foos, foo)

Why is this happening?

Update #1

I've implemented the == function but I still get the same error. Am I doing this improperly?

class Foo {}

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}

let foo = Foo()
let foos = [Foo(), Foo()]

contains(foos, foo)

Upvotes: 2

Views: 4702

Answers (3)

Aaron Rasmussen
Aaron Rasmussen

Reputation: 13316

Also note that there is always a way to check for an instance of a class in an array, even if you haven't implemented the equatable protocol or inherited from NSObject. You can use reduce to do it like this:

class MyClass { }
let myClass1 = MyClass()
let myArray = [myClass1]

let lookForMyClass1 = myArray.reduce(false) { $0 || $1 === myClass1 }

println(lookForMyClass1)  // Outputs: true

And you can generalize that to look for any class object in any SequenceType by overloading contains with something like this:

func contains<T: AnyObject, S: SequenceType where T == S.Generator.Element>(#haystack: S, #needle: T) -> Bool {
    return reduce(haystack, false) { $0 || $1 === needle }
}

Now you can call contains like this:

let searchForMyClass1 = contains(haystack: myArray, needle: myClass1)

println(searchForMyClass1)  // Output: true

Upvotes: 0

Gabriele Petronella
Gabriele Petronella

Reputation: 108111

Classes don't automatically inherit any equality logic from the base classs, so you need to be explicit and have Foo to conform to the Equatable protocol.

Actually, the only sensible equality the compiler could derive from that class declaration is the identity, and you probably don't want that.

Please note that

class Foo {}

is not the same as

class Foo : NSObject { }

By inheriting from NSObject you also inherit the default implementation of isEqual, which provides object identity equality.


Concerning your last update, you're only missing the Equatable protocol in the class definition. The following compiles just fine

class Foo : Equatable {}

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    // or simply
    // return lhs === rhs
}

let foo = Foo()
let foos = [Foo(), Foo()]

contains(foos, foo)

Or simply inherit from NSObject, which already provides identity equality

class Foo : NSObject {}

let foo = Foo()
let foos = [Foo(), Foo()]

contains(foos, foo)

Upvotes: 5

Rob Napier
Rob Napier

Reputation: 299345

The only function signature for contains is:

func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool

Foo is not Equatable, so it doesn't match this signature.

Upvotes: 2

Related Questions