この記事の要点
- Embedded Swift は、マイクロコントローラのような制約の厳しい環境でも動くように設計された Swift のサブセットです。専用のコンパイルモードによって、通常の Swift よりはるかに小さなバイナリを生成しつつ、言語のほとんどはそのまま使えます。本記事は、Swift 6.3 での改善に続いて、Swift 6.4 に入る追加の改善をまとめたものです。
- 言語面では、これまで
AnyObject制約付きに限られていた existential(any)型が一般化され、Any自体も含めてすべてのany型が使えるようになりました。これに伴い、untyped throws(throwsに型を付けない形)とメタタイプ(Int.selfなど)も完全にサポートされます。 - ライブラリ面では、文字列からの浮動小数点数のパース(
Double("3.14")など)と、並行処理でのエラーハンドリング(throwするタスクやタスクグループ)が Embedded Swift でも使えるようになりました。 - これらの動的な言語機能は、使った箇所でのみ実行時性能やコードサイズに影響します。サイズや性能に敏感なコードは、
PerformanceHints診断グループの警告を有効にして、こうした機能の使用を避けられます。
言語の改善
existential(any)型の一般化
これまで Embedded Swift は、AnyObject 制約を持つ existential(any)型しかサポートしていませんでした。つまり、クラスのインスタンスに対してしか使えませんでした。Swift 6.4 では、Any 自体を含めてすべての any 型が使えるようになります。
protocol P {
func method()
}
extension Int: P {
func method() { print("\(self) is here") }
}
let a: any P = 17
a.method() // prints "17 is here"
ただし、Embedded Swift のジェネリクスのコンパイルモデルは、すべてのジェネリック関数・型が最終的に特殊化されることを前提とします。このため any 型の使い方には制約があり、any 型の値に対してジェネリック関数を呼び出すことはできません。
extension P {
func genericMethod<T: P>(_ other: T) { ... }
}
let a: any P = 17
a.genericMethod(a) // error: cannot use generic instance method 'genericMethod' on a value of type 'any P' in Embedded Swift
untyped throws
これまで Embedded Swift では、次のように具体的なエラー型を指定する typed throws しか使えませんでした。
func parseRecord() throws(ParsingError) -> Record { ... }
任意の Error 適合値を throw できる「untyped」な throws は、これまで Embedded Swift では禁止されていました。
func loadImage() throws -> Image { ... } // これまで Embedded Swift では不可
untyped throws は、実質的に any Error 型の値を throw することと同じです。any 型の一般化により、Embedded Swift でも untyped throws が完全にサポートされます。ただし any Error の値を throw するには通常ヒープ割り当てが必要になるため、ヒープ割り当てを避けたいコードでは引き続き typed throws を優先すべきです。
メタタイプ
Embedded Swift では、メタタイプ(Int.self など)はごく限られた場所でしか使えませんでした。たとえば、ジェネリック関数の引数の型を指定する用途です。
rawPointer.bindMemory(to: Value.self, capacity: 1)
Swift 6.4 は、Embedded Swift でメタタイプを完全にサポートします。メタタイプのインスタンスを生成・利用でき、any (DefaultInitializable.Type) のような existential 型も扱えます。次の例は、通常の Swift と同じように動作します。
protocol DefaultInitializable {
init()
}
extension Int: DefaultInitializable { }
let factory: any (DefaultInitializable.Type) = Int.self
let aValue: any DefaultInitializable = factory.init()
ライブラリの改善
浮動小数点数のパース
Swift では、文字列から浮動小数点数の値をパースできます。
let inputText: String = getInputText()
if let value = Double(inputText) {
// value は Double
}
この機能を Swift で再実装した結果、これらの浮動小数点数のパース API が Embedded Swift でも使えるようになりました。
並行処理のエラーハンドリング
Embedded Swift の並行処理ライブラリが、throw する操作(throw するタスクやタスクグループなど)をサポートするようになりました。
let task = Task {
if badThing {
throw MyError.badThingHappened
}
return "ok"
}
print(try await task.value)
試してみる
これらの機能は、Swift development snapshot で今すぐ試せます。始めるには、さまざまなハードウェア上で Embedded Swift をビルド・実行できるサンプルプロジェクトを集めた Swift Embedded Examples リポジトリから入るのがおすすめです。
関連リンク
- Swift 6.3 でやってくる Embedded Swift の改善 — 前回のリリースでの Embedded Swift 改善
- A Vision for Embedded Swift — Embedded Swift の構想
- SE-0413(Typed throws) — エラー型を指定する
throwsの仕組み - Swift Embedded Examples — さまざまなハードウェア向けの Embedded Swift サンプル集
- PerformanceHints 診断グループ — 動的な言語機能の使用箇所を警告で洗い出す