iOSDC Japan 2025 アンドパッドSwiftクイズ解説 – Day1 午前

こんにちは、西 @jrsaruo_tech です。
iOSDC Japan 2025 Day1 前半戦です!

アンドパッドのブースでは本日も新しいクイズを出題しています。

  • Day0
  • Day1 午前 ← 本記事
  • Day1 午後
  • Day2 午前
  • Day2 午後

本記事ではDay1 午前に出題したクイズ4題を解説します。

それぞれの解答と解説はセクションを閉じているので、まだ解かれていない方はぜひチャレンジしてみてください。

※ クイズで使用しているSwiftのバージョンは6.1.2、言語モードは6です。

% swift --version   
swift-driver version: 1.120.5 Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)
Target: arm64-apple-macosx15.0

Q1 ★☆☆☆☆

問題

変数valueの値を文字列に挿入するコードとして正しいものを選んでください。

  • A

      "number is $value"
    
  • B

      "number is ${value}"
    
  • C

      "number is \value"
    
  • D

      "number is \(value)"
    

解答と解説

解答

D

解説

Swiftで変数valueの値を文字列に挿入するにはDの"number is \(value)"という記法を用います。

let value = 10
print("number is \(value)") 

Q2 ★★☆☆☆

問題

/* ? */に当てはめてコンパイルが通るものを選んでください。

struct FooError: Error {}

func foo() throws {}

do {
    try foo()
}  {
    print("FooError")
} catch {
    print(error)
}
  • A

      catch FooError
    
  • B

      catch FooError()
    
  • C

      catch is FooError
    
  • D

      catch as FooError
    

解答と解説

解答

C

解説

FooError型のエラーを個別に捕捉するための書き方を問うクイズです。

catch句にはパターンを書きます。パターンとは値の構造を表したもので、値のマッチや構造の分解に用いられます。小難しいことを言いましたが簡単に言えばswitch文のcaseに書けるようなやつです。

パターンは色々あるので、興味がある方は以下のページをご覧ください。
The Swift Programming Language – Patterns

switch value {
case is Foo: 
    break
case 1...5: 
    break
case _: 
    break
}

選択肢のなかでコンパイルが通るのはCです。is FooErrortype-castingパターンと呼ばれ、対象の値(ここでは投げられたエラー)がFooError型であればマッチします。

選択肢AのFooError(型名のみ書いたもの)や選択肢Dのas FooErrorのようなパターンは存在せず、コンパイルが通りません。asを使ったtype-castingパターンは正しくはcatch let error as FooErrorのような形です。この場合は対象の値がFooError型であればマッチし、FooError型にキャストされた値が変数errorにバインドされます。

選択肢Bは少し特殊です。FooError()expressionパターンとして解釈できますが、expressionパターンとのマッチングではそれに対応する~=演算子(パターンマッチング演算子)のオーバーロードが必要となります。ここでは対象の値any ErrorとexpressionパターンFooError()とのパターンマッチングを担う以下のような~=演算子が必要です。

func ~= (pattern: FooError, value: any Error) -> Bool

このようなオーバーロードはデフォルトでは提供されていないため、自前で実装しない限りcatch FooError()はコンパイルが通りません。

ここで、enumで定義したエラー型なら特に何もしなくてもcatch EnumError.fooのように書けるのでは?と思った方もいるかもしれません。確かにこれはコンパイルが通るのですが、実はこれはenumeration caseパターンという別のパターンとして解釈されています。そちらでは上記のような~=演算子は必要とされません。

Q3 ★★★☆☆

問題

次のコードのコンパイルを通すために、MyIntが最低限適合しなければならないプロトコル(必要最低限の実装だけ要求されるもの)を選んでください。

struct MyInt {
    var value: Int
}

let five: MyInt = 5
  • A: BinaryInteger
  • B: Numeric
  • C: IntegerLiteralType
  • D: ExpressibleByIntegerLiteral

解答と解説

解答

D

解説

50.5"abc"truenilなど、ソースコードに直接書かれた値そのものをリテラルと呼びます。

このクイズは、独自の型MyInt5のような整数リテラルで初期化できるようにするための方法を問う問題です。

実はSwiftのリテラル自体には型がありません。5と書いたからといってInt型であるとは限らず、"a"と書いたからといってString型であるとは限りません。

let a = 5と書いたときにaInt型と推論されるのは、整数リテラルがデフォルトでInt型として推論されるようになっているからです。以下のコードも、5というIntInt8型に変換しているわけではなく、5というリテラルから直接Int8型の値を作っています。

let a: Int8 = 5

そしてこの機能を実現しているのは各リテラルに対応するExpressibleByXXLiteralというプロトコルです。例えば整数リテラルにはExpressibleByIntegerLiteral、文字列リテラルにはExpressibleByStringLiteralが対応します。

ExpressibleByIntegerLiteralに適合させた型は整数リテラルで値を初期化することができるようになります。というわけで答えはDです。

extension MyInt: ExpressibleByIntegerLiteral {
    init(integerLiteral value: IntegerLiteralType) {
        self.value = value
    }
}

let five: MyInt = 5 

選択肢AのBinaryIntegerは2進数で表現される整数を表すプロトコル、選択肢BのNumericは掛け算(と足し算)ができる値を表すプロトコル、選択肢CのIntegerLiteralTypeはプロトコルではなくIntの型エイリアスです。

ちなみにBinaryIntegerNumericを、NumericExpressibleByIntegerLiteralを継承しているため、実はMyIntをこれらのいずれかに適合させればlet five: MyInt = 5はコンパイルが通ります。とはいえ問題文には「最低限適合しなければならないプロトコル」とあるので、正解はやはりExpressibleByIntegerLiteralです。

IntegerLiteralTypeは上書きすると面白いことが起きるのですが、それはまた別の機会に。

Q4 ★★★★☆

問題

次のなかでコンパイルが通るコードをすべて選んでください。
ただし、独自の==関数は定義されていないものとします。

  • A

      func checkEquality(_ lhs: AnyHashable, _ rhs: AnyHashable) -> Bool {
          lhs == rhs
      }
    
  • B

      func checkEquality(_ lhs: any Equatable, _ rhs: any Equatable) -> Bool {
          lhs == rhs
      }
    
  • C

      func checkEquality(_ lhs: some Equatable, _ rhs: some Equatable) -> Bool {
          lhs == rhs
      }
    
  • D

      func checkEquality(_ lhs: T, _ rhs: T) -> Bool where T: Equatable {
          lhs == rhs
      }
    

解答と解説

解答

A、D

解説

各選択肢を検討する前に、==演算子について確認しておきましょう。

ある型Aのインスタンスa、型Bのインスタンスbについて考えます。これらの間でa == bのように==演算子を利用できるかどうかは、それに対応する==演算子のオーバーロード実装があるかどうかで決まります。

func == (lhs: A, rhs: B) -> Bool { ... }


extension A {
    static func == (lhs: A, rhs: B) -> Bool { ... }
}


(==)(a, b) 

そして、Equatableプロトコルに適合した型は後に示す==演算子のオーバーロード実装を持っています。

したがって、lhs == rhsが通るかどうかは

  • Equatable由来の==実装が利用できるか
  • またはEquatable由来でない==実装が提供されているか

の2点を確認すれば良いです。そして今回の選択肢において後者は提供されていないので、前者の「Equatable由来の==実装が利用できるか」だけをチェックすればOKです。

ではその条件を調べるためにEquatableの定義を見てみましょう。

public protocol Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool
}

Equatableが要求する==メソッドには、lhsrhsがいずれも同じSelf型であるという制約があります。SelfとはEquatableに適合した型自身です。重要なのは両辺が同じ型であることです。

extension Foo: Equatable {
    
    static func == (lhs: Foo, rhs: Foo) -> Bool { ... }
}

この制約により、例えばInt同士やString同士の比較はできる一方、IntString間での比較はできないようになっています。

10 == 10 
"a" == "a" 
10 == "a" 

以上を踏まえると、lhs == rhsという式において

  • lhsの型TEquatableに適合している
  • rhsの型もTである

という条件を共に満たしていれば、以下のようなEquatable由来の==実装が利用できるためコンパイルが通ります。

static func == (lhs: T, rhs: T) -> Bool { ... }

それではこれらの条件①②をふまえて選択肢を見ていきましょう。

A:OK

以下のように条件①②を共に満たしているためコンパイルが通ります。

  • lhsの型AnyHashableEquatableに適合している
  • rhsの型もAnyHashableである

繰り返しになりますがAnyHashableEquatableに適合しているため必ず以下の==メソッドを持っており、これを利用できるわけですね。

static func == (lhs: AnyHashable, rhs: AnyHashable) -> Bool

B:NG

lhsの型any EquatableEquatableに適合していないため、条件①を満たせずコンパイルが通りません。

なぜany EquatableEquatableに適合していないのかの説明は割愛しますが、気になる方は”self-conformance”というワードで調べてみてください。

C:NG

lhsの型some EquatableEquatableに適合しているため、条件①は満たされます。

そして一見lhsrhsはどちらも同じsome Equatable型に見えるので条件②も満たされそうですが、some Equatableとはあくまで「Equatableに適合した何か」でしかなく、lhsrhsが同じ型であるとは限りません。例えばlhsにはInt型、rhsにはString型の値が渡ってくるかもしれません。

func checkEquality(_ lhs: some Equatable, _ rhs: some Equatable) -> Bool { ... }


func checkEqualityT1, T2>(_ lhs: T1, _ rhs: T2) -> Bool where T1: Equatable, T2: Equatable { ... }


checkEquality(10, "a")

したがって条件②を満たしておらずコンパイルが通りません。「lhsrhsは同じ型である」という制約を課すためには、次の選択肢Dのように型パラメータを導入する必要があります。

D:OK

以下のように条件①②を共に満たしているためコンパイルが通ります。

  • lhsの型TEquatableに適合している
  • rhsの型もTである

今回も長くなってしまいましたが、ここまで読んでくださりありがとうございました。
感想等お待ちしております。

午後も新しいクイズが出題されるのでぜひまたアンドパッドブースまで遊びにきてください!

Day2にはTrack Bにて11:25より『ネイティブ製ガントチャートUIを作って学ぶUICollectionViewLayoutの威力』というタイトルで登壇しますので、そちらもぜひお越しください。

fortee.jp

さらにLTにも弊社やまひろ @yamahiro248 が登壇します。こちらもお楽しみに!

fortee.jp

引き続きiOSDC楽しんでいきましょう!




元の記事を確認する

関連記事