前書き(という名の無駄話
タイトル結構悩んだ。ぶっちゃけシンプルにまとめられそうなタイトル思いつかなかったのでネタに走ろうと思った。そして「init の形」とどっち選ぶか悩んだけど最終的に「ネタのわかりやすさ」と「やってることのわかりづらさ」でこっちの名前にしました。反省はしていない。
本編
とあることやろうとしてて、「そういえば init
って名前の static func
作ったらどうなるんだろ?」っと思って、こんな闇深いソースを作ってみたら意外とちゃんと(?)動いた。
extension Int {
static func `init`(float: Float) -> Float {
return float
}
}
let float = Int(float: 3.14)
print(float) // 3.14
やろうとしてること読んでわかるよね?Int
の extension
で init
って名前の返り値が Float
の static func
作って、それをそのまま Int()
といういかにも Int
のイニシャライザーを呼んでる形の呼び出しを書いたらちゃんと Float
で生成されたという黒魔術。「ちゃんと」っていうとなんか変だけど。ちなみに今日ツイッターで呟いたら思ったより拡散されて多分みんなも闇だと感じてるんじゃないかと。なるほど init
って名前の static func
作っちゃうとそれが「イニシャライザー」(?)として使えるんだね。
一応説明
いかにも闇深そうなコードを面白半分で紹介しましたが、もちろん本来の意図はこんな闇深いことをやりたかったわけではありません。きちんとした理由があるのです。
最近この記事に刺激されて、確かに衝突するのも回避したいし何よりこの書き方がとても好きで自作のフレームワークにもこういう書き方に対応したいなぁと思いつつ、Eltaso の修正にかかってるのですが、もともと作ってた CGPoint(point: CGFloat)
のイニシャライザーがどうすればいいんだろ、自分自身の型とは違う型のイニシャライザーって作れないのかな?まあ最悪作れなくても .init(point: CGFloat)
で書ければいいかな、ってノリでとりあえず static func init
を作ってみたらそれがちゃんとうまく行ったんですね。そこでふっと思ったんですよ。じゃあ (point: CGFloat)
って使えないのかな?って。そしたら Playground で書いてみたら動いた。
ちなみに今改造中のコードはこんな感じです:
public protocol EltasoCompatible {
associatedtype Eltaso
var eltaso: EltasoContainer<Self> { get }
}
extension EltasoCompatible {
public var eltaso: EltasoContainer<Self> {
return EltasoContainer(body: self)
}
}
public struct EltasoContainer<Containee> {
public let body: Containee
}
extension CGPoint: EltasoCompatible {
public typealias Eltaso = EltasoContainer<CGPoint>
}
extension EltasoContainer where Containee == CGPoint {
public static func `init`(point: CGFloat) -> CGPoint {
return CGPoint(x: point, y: point)
}
}
let point = CGPoint.Eltaso.init(point: 3.14)
print(point) //(3.14, 3.14)
ね?綺麗でしょ?(自画自賛