12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Type Omission - 呼び出し時に型名を省略できるstatic fieldの話

Last updated at Posted at 2016-03-05

前置き

try! Swiftお疲れ様でした!

try! Swift 資料まとめ - Qiitaniwatakoさんの書き起こし のおかげで、在宅組にもかかわらず沢山の知見を得ることができました。

さて、この記事では

try! Swift Swiftヒップスター #tryswiftconf Day3-4 - niwatakoのはてなブログ

にある1枚のスライド


struct PowerLevel {
	let value: Int
	static func determinePowerLevel(_ fighter: ZFighter) -> PowerLevel
}

func powerLevelIsOver9000(powerLevel: PowerLebel) -> Bool {
	return powerLevel.value > 9000
}

//Type omission!
if powerLevelIsOver9000(.determinePowerLevel(goku)) {
	print("It's over 9000!")
}

enumの中の静的変数はenumを返してくる、同じことをstatic func でやっている。
static var が enum のように動くなら、タイプを省略できます

について詳しく調べてみようと思います。

疑問

「あーね、わかるよ、Enumで .Hoge とか書けるのと同じだよね。 init(frame: CGRect.zero)init(frame: .zero) って書けるやつだよね」
とあっさり納得しそうになったんですが、よく考えると不思議ですよね。
frameがCGRectなのは自明だから型推測が走る…のは分かる。
どこかの型に .zero: CGRect が定義されていることまでは分かるだろうけれど、定義されているのがCGRectだということはどうやって判断するんだ?
Selfを返す場合には省略できるというルールなのか…?

  • Type Omissionは、Selfを返す場合のみ有効?
  • Type Omissionは、継承関係のあるclassでも有効なのだろうか?
  • Type Omissionは、サブクラスではどう動くのか?

これらの各疑問を、コードとともに検証します。

疑問1. Type Omissionは、Selfを返す場合のみ有効?

検証コード

struct A {
    static var aToA: A { return A() }
    static var aToB: B { return B() }
}

struct B {
    static var bToA: A { return A() }
    static var bToB: B { return B() }
}

func doSomething(a: A) {}

doSomething(A.aToA)
doSomething(B.bToA)
doSomething(.aToA)
doSomething(.bToA) // error: Type 'A' has no member 'bToA'

なるほど、4つ目でビルドエラーになりました。

doSomething は引数として型 A を取ります。
ここでコンパイラは、 AaToA: A, bToA: A が定義されていることを期待するわけですね。
実際に bToA: A が定義されているのは B ですが、コンパイラが辿るのはあくまで A だけなのだとわかりました。

疑問2. Type Omissionは、継承関係のあるclassでも有効なのだろうか?

検証コード
C を継承したサブクラス CC を作ります。

class C {
    static var c: C { return C() }
    class var c_inheritable: C { return C() }
}
class CC: C {
    override class var c_inheritable: C { return CC() }
}

func doWithC(c: C) {
    print(c)
}

doWithC(.c) // C
doWithC(.c_inheritable) // C

いけましたね! 親まっしぐら。
型として要求されているのは C なので、サブクラスがどうなってようとガン無視です!

疑問3. Type Omissionは、サブクラスではどう動くのか?

検証コード
C を継承したサブクラス CC1CC2 を作ります。

class CC1: C {
    override class var c_inheritable: CC1 { return CC1() }
}
class CC2: C {
    override class var c_inheritable: C { return CC2() }
}

func doWithCC1(cc: CC1) {
    print(cc)
}
func doWithCC2(cc: CC2) {
    print(cc)
}

doWithCC1(.c_inheritable) // CC1
doWithCC2(.c_inheritable) // error: type of expression is ambiguous without more context

doWithCC1 では、コンパイラは、求められている型 CC1c_inheritable: CC1 が定義されているか探します。
c_inheritable: C は、継承にあたって、より詳細に c_inheritable: CC1 と書くことができます。
期待通りfieldが見つかり、無事、コードは解釈されました。

一方、doWithCC2 では、求められている型 CC2c_inheritable: CC2 が定義されているか探します。
しかしビルドエラーが発生。なぜなら、定義されているのは c_inheritable: C だからです。

doWithCC2(.c_inheritable as! CC2) // error: type of expression is ambiguous without more context

こう書けば通るかも?と思ったんですが、ダメでした。キャストの問題ではなく、fieldの探索の問題ということですね。

まとめ

  • 推測された型にSelfを返すstatic fieldがある場合、呼び出し時には型名を省略することができる。
  • サブクラスでType Omissionを働かせるためには、戻り値の型もSelfに合わせる必要がある。
12
11
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
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?