[Swift] AnyStringが必要ない理由。

# はじめに

## 概要

標準ライブラリに[AnySequence](https://developer.apple.com/documentation/swift/anysequence)や[AnyHashable](https://developer.apple.com/documentation/swift/anyhashable)はあるのに、`AnyString`は無い…。自分で実装しても良いけど…本当に必要かな？　と考えたときのことをつらつらと。


## 想定対象読者 ~~(何を偉そうに)~~

Swiftでtype erasure(型消去)の存在と目的を知って、これから実装しようという人。もしくはたくさん実装して飽きちゃった人。


# Type erasureが必要なとき

　プロトコルをそのまま型名として使えるのは、そのプロトコルが`associatedtype`も`Self`も必要としていないときですよね。逆に、それら(のうち少なくともどちらか)を必要条件とするプロトコルはそのまま型名として使えないので、type erasureの出番となるわけです。
　具体的な実装パターンは[@omochimetaru](https://qiita.com/omochimetaru)さんの「[Swift の Type Erasure の実装パターンの紹介](https://qiita.com/omochimetaru/items/5d26b95eb21e022106f0)」という記事が大変勉強になります。
　たとえば、`SomeType`というassociatedtypeを持つプロトコル`P`のtype erasureを「継承box方式」で実装すると…

<details>
<summary>継承Box方式のコード</summary><div>

```Swift
public protocol P {
  associatedtype SomeType
  func f(_: SomeType)
}

public struct AnyP<SomeType> {
  private class _Box {
    func f(_: SomeType) {
      fatalError("Must be overridden.")
    }
  }

  private class _SomeP<T>: _Box where T: P, T.SomeType == SomeType {
    private let _base: T
    
    init(_ base: T) {
      self._base = base
    }

    override func f(_ something: SomeType) {
      self._base.f(something)
    }
  }

  private let _box: _Box

  public init<T>(_ someP: T) where T: P, T.SomeType == SomeType {
    self._box = _SomeP<T>(someP)
  }

  public func f(_ something: SomeType) {
    self._box.f(something)
  }
}

public struct Example: P {
  public typealias SomeType = String
  
  public func f(_ string: String) {
    print(string)
  }
}

let anyP = AnyP<String>(Example())
anyP.f("Type Erasure!") // -> prints "Type Erasure!"
```

</div></details>
…うん、めんどくさい。自動でtype erasureのコードを書いてくれる機能があったらいいのに。




# ひとまず`AnyString`を実装してみようかな…？

　対象となるプロトコルは[StringProtocol](https://developer.apple.com/documentation/swift/stringprotocol)です。`StringProtocol`はassociatedtypeもSelfもあるので、そのまま型名としては使えません。
　となると、type erasureを実装したくなりますよね。ですよね？
　でも、その前に…


## `StringProtocol`とは…？

　[Apple Developer Documentation](https://developer.apple.com/documentation/swift/stringprotocol)では*"A type that can represent a string as a collection of characters."*と書いてあります。要は「文字列」を表す型のためのプロトコルですね。
　そして、さらにOverviewには*"Do not declare new conformances to StringProtocol. Only the String and Substring types in the standard library are valid conforming types."*と書いてあります。なんと、**`StringProtocol`に準拠するのは`String`と`Substring`のみ**で、ほかの型は`StringProtocol`に準拠させてはいけないのです。


## 強引なType Erasure

　[別記事](https://qiita.com/YOCKOW/items/d670a347757d4bc0e812#%E5%8D%B4%E4%B8%8B%E6%A1%88)で少し書いたのですが、準拠型(conforming types)の種類が有限の場合は、強引にtype erasureを定義することができます。具体的にはAssociated Valuesを持った`enum`を定義すればいいのです。
　その方法で`StringProtocol`のtype erasureを実装するとすると…

```Swift
public enum AnyString {
  case string(String)
  case substring(Substring)
}
```

　これだけです。こういうtype erasureもあり得ます。
　…いやまぁ、型を定義するだけならこれだけなんですが、いろんなメソッドを実装しようとすると、一つ一つ書いていかなくてはいけませんがね。
　実装のめんどくささは、Box方式とかとあまり変わりないかもしれません。


## と、ここで、手段があるのはわかったけど、目的は何だった？

　`StringProtocol`のtype erasureを実装しようとその手段を考えているわけですが、ここで一旦、その目的の方を考えてみましょう。
　`StringProtocol`は型名に使えないからtype erasureが必要なんですよね。では、型名に使いたい時ってどんな時でしょう？
　たとえば…


```Swift
struct MyString {
  var string: AnyString
}
```

　というように、`MyString`みたいな独自の型のプロパティとして使いたい時とかですよね。具体的な型を隠蔽する型を用意することでプロパティの型名として使うことができるわけです。
　でも、プロパティの型名に使う時、必ず具体的な型を隠蔽しなくてはいけないのでしょうか…？



# `AnyString`を使わない方法とは？

　具体的な型名を隠蔽しないとしたら…？　でも`StringProtocol`そのものは型名に使えないし…。
　ここで思い出して欲しいのは、`StringProtocol`に準拠するのは`String`と`Substring`のみということです。そして、その上で`String.SubSequence == Substring`かつ`Substring.SubSequence == Substring`という関係にも気づくことができれば、答えは見えてきます。
　そう、**型を`Substring`に固定**してしまえば良いのです。
　具体的なコードは次のようになります：

```Swift
struct MyString {
  var string: Substring
}
```

　イニシャライザを追加しないと不便ですね。

```Swift
struct MyString {
  var string: Substring
  
  init<S>(_ string: S) where S: StringProtocol, S.SubSequence == Substring {
    self.string = string[...]
  }
}
```

　`string[...]`で、全範囲の`Substring`を作っています[^unbounded-range](`String`も`Substring`もその`SubSequence`が`Substring`だからできることですね)。

[^unbounded-range]: `string[string.startIndex..<string.endIndex]`と同じ。

　要は、型消去ならぬ型変換です。型変換といっても、`String`→`Substring`の変換とその`Substring`における操作はコストが低いので安心です。Appleのドキュメントには*"Operating on substrings is fast and efficient because a substring shares its storage with the original string. "*と書かれています。
　こうすれば、Type erasureを実装する手間も必要ありません。前述の通り実行時のコストもさして気にしなくて大丈夫です(Type erasureも、"Boxクラス"のインスタンスを作成する実行時コストが必要)。


# 応用例

　なにもこの話は`StringProtocol`に限った話ではありません。たとえば、[`BidirectionalCollection`](https://developer.apple.com/documentation/swift/bidirectionalcollection)に準拠する`MyArray`を実装し、かつその`SubSequence`も`MyArray`そのものにしたい場合…

```Swift
struct MyArray<Element>: BidirectionalCollection {
  var array: ArraySlice<Element>
  // 以下、プロトコルに準拠するためのコード
}
```

と実装すると楽なことがあります[^array-slice]。

[^array-slice]: `ArraySlice`の`startIndex`が`0`とは限らないことに注意は必要ですが。



# おわりに

　というわけで、**「ある特定の型への変換コストが高くない場合には、type erasureを実装するのではなく、その特定の型を利用するという選択肢もありだ」**というお話でした。