Swift3.0
Swift3.0 では以下のように Array を直接拡張しようとすると error: same-type requirement makes generic parameter 'Element' non-generic
と言われてできない。
extension Array where Element == Book {
...
}
調べてみたら以下の2つのやり方で実現できそうだった。
1. Collection または Sequence を拡張する
struct Book {
let title: String
let author: String
}
extension Collection where Iterator.Element == Book {
subscript(title title: String) -> Book? {
return filter{ $0.title == title }.first
}
subscript(author author: String) -> Book? {
return filter{ $0.author == author }.first
}
}
let books = [
Book(title: "the starry rift", author: "james tiptree, jr"),
Book(title: "hello summer, goodbye", author: "michael coney"),
]
// タイトルが一致する Book を取得
books[title: "the starry rift"]?.author // "james tiptree, jr"
// 著者が一致する Book を取得
books[author: "michael coney"]?.title // "hello summer, goodbye"
let notBooks = [1, 2, 3]
// Array<Book> ではないのでエラー
notBooks[title: "1"] // error: ambiguous reference to member 'subscript'
上記の例では filter くらいしか使ってないので Sequence を拡張しても正しく動くはず。
ただ、このやり方だと「Collection を実装していて Element が Book」の型が Array 以外に存在したときに意図しない挙動をしそうで怖い。なさそうだけど。
2. 対応する protocol を作って Element がその protocol に準拠しているときだけ拡張する
protocol BookType {
var title: String { get }
var author: String { get }
}
struct Book: BookType {
let title: String
let author: String
}
extension Array where Element: BookType {
subscript(title title: String) -> Book? {
return filter{ $0.title == title }.first as? Book
}
subscript(author author: String) -> Book? {
return filter{ $0.author == author }.first as? Book
}
}
let books = [
Book(title: "the starry rift", author: "james tiptree, jr"),
Book(title: "hello summer, goodbye", author: "michael coney"),
]
// タイトルが一致する Book を取得
books[title: "the starry rift"]?.author // "james tiptree, jr"
// 著者が一致する Book を取得
books[author: "michael coney"]?.title // "hello summer, goodbye"
let notBooks = [1, 2, 3]
// Array<Book> ではないのでエラー
notBooks[title: "1"] // error: cannot subscript a value of type '[Int]'
KickStarter-Prelude のコードを読んでいるときに見つけた。
https://github.com/kickstarter/Kickstarter-Prelude/blob/master/Prelude/Array.swift
Array 自体を拡張できるのはよいけど、このためだけに protocol を定義するのはちょっとやり過ぎ感があるなぁ...
Swift3.1
Swift3.1 では最初に書いたような書き方ができるっぽい。やりたいことをストレートに表現できてよさそう。はやくこれになりたい。
extension Array where Element == Book {
...
}