Eneko Alonso
Eneko Alonso

Reputation: 19722

Swift Compiler Error: The enum case has a single tuple as an associated value, but there are several patterns here

Building a project in Xcode 11.4 beta 3, I'm getting this Swift Compiler error on an enum:

The enum case has a single tuple as an associated value, but there are several patterns here, implicitly tupling the patterns and trying to match that instead

Source code:

switch result {
case .error(let err):
    //
case .value(let staff, let locations):  // <-- error on this line
    //
}

Result is an generic enum with associated values for .error and .value. In this case, the associated value is a tupple.

public enum Result<T> {
    case value(T)
    case error(Error)
}

Don't recall seeing this error before, and searching for it did not yield any results. Any ideas?

Upvotes: 21

Views: 10169

Answers (7)

I think instead check the type and get the value in a tuple, you check the value of the condition. This is my example

switch result {
case let .error(err):
// Do something
case let .value(staff, locations):  // <-- error on this line
// Do something
}

Upvotes: 0

Eneko Alonso
Eneko Alonso

Reputation: 19722

Ok, figured it out. Seems like enum with associated values, where the value type is a tuple, can no longer be matched on a switch statement like that:

// Works on Xcode 11.3.1, yields error on 11.4 (Swift 5.2)
switch result {
case .error(let err):
    //
case .value(let staff, let locations):  
    //
}

Solution

Values from tuple have to be manually extracted in Xcode 11.4 (Swift 5.2):

// Works on Xcode 11.4
switch result {
case .error(let err):
    //
case .value(let tuple):  
    let (staff, locations) = tuple
    // 
}

Upvotes: 21

dimohamdy
dimohamdy

Reputation: 3053

Swift 5.3

A clean way using typealias because may you have a lot of value in tuple so typealias make the switch case more cleaner

public typealias StaffLocationsData = (let staff, let locations)

switch result {
case .error(let err):
    //
case .value(let data):  
    let staffLocationsData: StaffLocationsData = data
    print(staffLocationsData.staff)
    print(staffLocationsData.locations)
}

Upvotes: 0

matt
matt

Reputation: 535860

In Xcode 12 / Swift 5.3, the warning message is greatly improved:

Enum case 'value' has one associated value that is a tuple of 2 elements. Replace (let staff, let locations) with ((let staff, let locations)).

That's far clearer. It tells you what to do, and offers to do it, as a Fix-It. Of course the alternative is to say (let tuple) and pass the tuple into the case's code and deal with it there, but there seems no need to say that explicitly in the warning.

Upvotes: 3

Paul Peelen
Paul Peelen

Reputation: 10329

If I may, I'd like to add an answer for the if case version too.

if case let .value(staff, error) = result {
    // Do something
}

and then of course ignoring case:

if case let .value(staff, _) = result {
    // Do something
}

Upvotes: 1

bolinhalouise
bolinhalouise

Reputation: 51

This is a known issue: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_release_notes

Code that relies on the compiler automatically tupling a pattern may lead to a compiler error when upgrading to Xcode 11.4, even though the code compiled before. (58425942)

For example, leaving out parentheses when switching on an Optional of a tuple type causes a compiler error:

switch x { // error: switch must be exhaustive
case .some((let a, let b), let c): // warning: the enum case has a
     // single tuple as an associated value, but there are several
     // patterns here, implicitly tupling the patterns and trying
     // to match that instead
...

}

Workaround: Add extra parentheses to explicitly tuple the pattern:

switch x {
case .some(((let a, let b), let c)): // Notice the extra pair of parentheses.
...

}

Upvotes: 5

Wernzy
Wernzy

Reputation: 1098

I found you can also silence this error by treating the associated value more like a tuple by wrapping it in an extra set of brackets:

switch result {
case .error(let err):
    //
case .value((let staff, let locations)):  
    //
}

Upvotes: 22

Related Questions