Catch an error based on its protocol's associated type?

Is there any way to cleanly accomplish the following?

protocol SomeError: Error {
	associatedtype Payload
	var payload: Payload { get }
}

struct SomeStringError: SomeError {
	let payload: String
}

do {
	throw SomeStringError(payload: "Test string")
}
catch let error as SomeError where SomeError.Payload == String {
	... // `String`-specific handling
}

The sample code above fails compilation with associated type 'Payload' can only be used with a concrete type or generic parameter base line, and nothing so far that I've tried has been able to get around that.

I know I can fall back to casting the payload value after just matching the error to the protocol itself, but it'd help code cleanliness a lot if I could match based on an associated type.

Best I've been able to get to so far:

extension SomeError { 
	var payloadType: Payload.Type { Payload.self } 
}

do { 
	throw SomeStringError(payload: "Test string") 
}
catch let error as SomeError where error.payloadType == String.self {
	...
}

Guess that'll do from a code-cleanliness standpoint for now, unless someone else knows another way to get at the associated type in a catch pattern-match.

Make the payload type a primary associated type:

protocol SomeError<Payload>: Error {
  // ...same contents as above
}
struct SomeStringError: SomeError {
  // ...same contents as above
}
do {
  // ...same contents as above
} catch let error as any SomeError<String> {
  // whatever you want to do with the payload string, e.g.:
  print(error.payload)
}
1 Like

Hmm I had tried that in the REPL and was getting "Execution interrupted", let me spin up a real project to test.

main.swift:

protocol SomeError<Payload>: Error {
	associatedtype Payload
	var payload: Payload { get }
}

struct SomeStringError: SomeError {
	let payload: String
}

do {
	throw SomeStringError(payload: "Test string")
}
catch let error as any SomeError<String> {
	print(error.payload)
}

Terminal:

% swift run
Building for debugging...
[3/3] Linking error-test
Build complete! (1.24s)
zsh: segmentation fault  swift run
% swift --version
swift-driver version: 1.62.8 Apple Swift version 5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)
Target: x86_64-apple-macosx12.0

Running macOS 12.6, if it matters - I know there's some runtime support coming in 13.0, maybe I need to wait for that?

Constrained existential types (any SomeError<String>) do indeed require runtime support.

2 Likes