LoginSignup
14
13

More than 3 years have passed since last update.

Swift3.0 で Array の Element が特定の型のときだけ extension したい

Last updated at Posted at 2017-01-11

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 {
...
}

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13