Implementing dynamic member lookup on an enum with associated values leads to build errors when there is more than one keypath function.
I have an enum with multiple associates values, as described below:
@dynamicMemberLookup
enum Media {
case movie(Movie)
case book(Book)
subscript<T>(dynamicMember keyPath: KeyPath<Entertainment, T>) -> T {
switch self {
case .movie(let movie):
return movie[keyPath: keyPath]
case .book(let book):
return book[keyPath: keyPath]
}
}
}
The structs Movie
and Book
inherit protocols Filmed
and Written
, which themselves inherit from the more generic protocol Entertainment
:
protocol Entertainment {
var isbn: Int { get }
var title: String { get }
var sales: Int { get set }
}
protocol Written: Entertainment {
var author: String { get }
}
protocol Filmed: Entertainment {
var actors: [String] { get set }
}
struct Movie: Filmed {
var isbn: Int
var title: String
var sales: Int
var actors: [String]
}
struct Book: Written {
var isbn: Int
var title: String
var sales: Int
var author: String
}
So far, this works and has allowed me to access variables such as isbn
and title
, which are defined in Entertainment
, from the top-level Media
object through the subscript keypath lookup.
let media: Media = .movie(Movie(isbn: 1011, title: "Top Gun", sales: 1000, actors: ["Tom Cruise", "Miles Teller"]))
print(media.title)
print(media.sales)
Now, when I try to add the subscript function below in Media
so that I can optionally access variables conforming to either Filmed
or Written
, I receive an error for the above print statements stating Ambiguous use of 'subscript(dynamicMember:)'
subscript<T>(dynamicMember keyPath: KeyPath<Filmed, T>) -> T? {
switch self {
case .movie(let movie):
return movie[keyPath: keyPath]
default:
return nil
}
}
In this example, the build errors are as follows:
let media: Media = .movie(Movie(isbn: 1011, title: "Top Gun", sales: 1000, actors: ["Tom Cruise", "Miles Teller"]))
print(media.title) // BUILD ERROR: Ambiguous use of 'subscript(dynamicMember:)'
print(media.sales) // BUILD ERROR: Ambiguous use of 'subscript(dynamicMember:)'
print(media.actors) // This doesn't throw an error and successfully logs
When I remove the second subscript function, the build errors are gone.
Please let me know if I'm doing anything wrong in my implementation here.
Figured out the answer:
By changing Filmed
and Written
to no longer conform to Entertainment
, the build errors were removed. The code as following now works:
protocol Entertainment {
var isbn: Int { get }
var title: String { get }
var sales: Int { get set }
}
protocol Written {
var author: String { get }
}
protocol Filmed {
var actors: [String] { get set }
}
struct Movie: Entertainment, Filmed {
var isbn: Int
var title: String
var sales: Int
var actors: [String]
}
struct Book: Entertainment, Written {
var isbn: Int
var title: String
var sales: Int
var author: String
}
@dynamicMemberLookup
enum Media {
case movie(Movie)
case book(Book)
subscript<T>(dynamicMember keyPath: KeyPath<Entertainment, T>) -> T {
switch self {
case .movie(let movie):
return movie[keyPath: keyPath]
case .book(let book):
return book[keyPath: keyPath]
}
}
subscript<T>(dynamicMember keyPath: KeyPath<Filmed, T>) -> T? {
switch self {
case .movie(let movie):
return movie[keyPath: keyPath]
default:
return nil
}
}
}
Here is the output of the print statements:
let media: Media = .movie(Movie(isbn: 1011, title: "Top Gun", sales: 1000, actors: ["Tom Cruise", "Miles Teller"]))
print(media.title) // OUTPUT: "Tom Cruise"
print(media.sales) // OUTPUT: 1000
print(media.actors) // OUTPUT: Optional<[String]>: ["Tom Cruise", "Miles Teller"]