Skip to content

When passing a collection to Set.contains(_:), forward to Set.isSuperset(of:) #796

@marcomasser

Description

@marcomasser

Motivation

There is a confusing behavior of Set.contains(_:) when passing a collection that contains more than one element:

let s: Set = [1, 2, 3]
s.contains([1]) // true
s.contains([1] as Set) // true
s.contains([1, 2]) // false 🤨
s.contains([1, 2] as Set) // false 🤨
s.contains([1, 2, 3]) // false 🤨
s.contains([1, 2, 3] as Set) // false 🤨

I think this confusion is introduced by a Collection extension in the _StringProcessing module:

extension Collection where Self.Element : Equatable {

    /// Returns a Boolean value indicating whether the collection contains the
    /// given sequence.
    /// - Parameter other: A sequence to search for within this collection.
    /// - Returns: `true` if the collection contains the specified sequence,
    /// otherwise `false`.
    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
    public func contains<C>(_ other: C) -> Bool where C : Collection, Self.Element == C.Element
}

I understand that this behavior is caused by the Set internally storing its elements in a random order (as viewed from outside), so checking whether “[…] the collection contains the given sequence” is not really a useful thing to do on a Set, making this method pretty much useless there.

Proposed solution

To make this method useful on Set, it could simply forward to Set.isSuperset(of:), which is what I would have expected in the first place:

s.isSuperset(of: [1, 2]) // true
s.isSuperset(of: [1, 2] as Set) // true
s.isSuperset(of: [1, 2, 3]) // true
s.isSuperset(of: [1, 2, 3] as Set) // true

While maybe not strictly correct in the sense of the documentation that “[…] the collection contains the given sequence” because the order of elements as passed is irrelevant, I think it is more useful because the behavior is at least deterministic.

Alternatives considered

None really. Do nothing and keep the behavior as-is, I guess.

Additional information

I posted on the Swift forum about this and received only one reply that agreed with the confusing behavior.

Here’s the thread: Passing a Collection to Set.contains(_:) leads to confusing results

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions