Sponsor Link
環境&対象
- macOS Monterey 12.3 Beta
- Xcode 13.2.1
- iOS 15.2
String と Substring
String は、文字を表現する型です。
関数の引数等にも、よく使われる型です。
その String に対して操作を行うと、Substring になる時があります。
例えば、次のコードでは、str を(カンマ区切りで)分解した文字列を tokens に保持しています。
この tokens には、Substring 型のデータが保存されています。
Substring 型のデータを String として扱いたい時には、Substring を引数として受け取る String initializer を使うと、String 型のデータを生成することもできます。
let str = "Hello,world"
let tokens = str.split(separator: ",")
print(tokens) // -> ["Hello", "world"]
print(type(of: tokens[0]) // -> Substring
let token0 = String(tokens[0])
print(type(of: token0) // -> String
以下の記事でも 説明しています。
[Swift] Swiftでも文字列操作 Substringの扱い方
Substring を引数として受け取る
引き渡し先の関数が、String しか受け付けない時はしょうがないのですが、ただ 参照するだけのケースでは、わざわざ String を生成して渡すのはすこし無駄な気がします。
Swift のライブラリ等は、Substring でも受け付けることができる関数が多く定義されています。
ですが、自分でそのような関数を作ろうとして Substring を受け付ける関数を作っても期待通りには動きません・・・・
実際に作ってみると以下のようなエラーとなる箇所が発生します。
func acceptStringAndSubstring(anyStr: SubString) { }
Error : cannot convert value of type 'String' to expected argument type 'Substring'
Substring を引数として呼び出す箇所はうまく動くのですが、String を引数として呼び出す箇所では 上記のエラーが発生します。意味は、”String を渡されたが、引数として必要な Substring に変換できない” ということです。
つまり、引数の型として、Substring を指定してしまうと、String が受け取れないということです。なんとなく、String と Substring は、自動相互変換されそうですが、そうではないということでした。
Note: 複数の関数を定義して それぞれが String と Substring を受け取るようにするのも一案ですが、スマートではありません。
String と Substring のどちらもが準拠する Protocol として StringProtocol があります
1つの関数で String と Substring のいずれも受け取れるようにするためには、この StringProtocl を使用して定義する必要があります。 → ですがエラーとなります。
func acceptStringAndSubstring(anyStr: StringProtocol) { }
Error: protocol 'StringProtocol' can only be used as a generic constraint because it has Self or associated type requirements
上記のエラーからもわかりますが、Swift では、Protocol を引数の型に直接指定することはできません。
使用するためには、Generics を使用して定義する必要があります。
具体的には 以下のように定義することで、String, Substring のどちらもが準拠する StringProtocol を引数の型として指定できることになります。
func acceptStringAndSubstring<T>(anyStr: T) where T: StringProtocol { }
上記のように定義することで、String, Substring いずれの型のデータであっても受け付けることが可能となります。
まとめ
- String, Substring のどちらもが準拠する StringProtocol を型に指定する
- Generics を指定して、StringProtocol を型に指定する必要がある
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link