型と推論
変数はcount: i32 = 10、関数の引数はcount: i32、戻り値は-> i32と書きます。ローカル変数の型は省略できますが、決まった型を再代入で変更することはできません。
| 型 | 用途 | 制約・値の扱い |
|---|---|---|
i8 / i16 / i32 / i64 |
符号付き整数。数字はビット数 | コピーできる。異なる数値型の演算には明示的な変換が必要 |
u8 / u16 / u32 / u64 |
符号なし整数 | コピーできる。負数を格納できない |
f32 / f64 |
小数を扱う浮動小数点数 | コピーできる |
bool |
True / False |
分岐・ループの条件に使う |
str / bytes |
UTF-8文字列 / バイト列 | 所有値。自作関数へ渡すとmoveする |
List[T] / [T] |
同じ型の要素を持つ配列 | 所有値。forはCopy要素をコピーし、非Copy要素を読み取り専用で借りる |
view[str] / view[bytes] / view[T] |
文字列・バイト列・配列を借りて読む | 元データの所有者が必要。読むだけで元の値は変更できない |
T? / Option[T] |
値がある、またはNone |
Noneには型の文脈が必要。Some / Noneのmatchで取り出す |
Result[T, E] |
成功値またはエラー | EはError・独自class・enum。tryで伝播、matchで処理する |
shared[T] |
複数の場所で共有する所有値 | shareで作り、clone_sharedで共有参照を増やす |
UUID / timestamp |
UUID / 時刻の値 | UUIDの文字列変換にはuuid_parse / uuid_formatを使う |
fn[引数の型..., 戻り値の型] |
関数を値として渡す | 同期関数の引数・戻り値に使える。最後の型が戻り値 |
unit |
戻り値がないことを表す型 | 関数の戻り値型を省略したときの型 |
count = 10はi64、rate = 1.5はf64です。型注釈がある整数literalはその範囲を確認します。現在の数値変換APIは、i8 / i16 / i32 / u8 / u16 / u32からの損失のないi64(value)と、i64から範囲を検査するi32(value) -> Result[i32, Error]です。任意型への汎用castはありません。
小数リテラルも、型注釈・引数・戻り値などで決まるf32またはf64の範囲を検査します。その型で無限大になる値は、型検査でエラーになります。
VS Code拡張では、変数名にマウスを置くと推論された型を確認できます。たとえばcountはcount: i64です。関数の引数やcaseの束縛名も対象です。エディターの操作例で試せます。
nullableはmissing: i64? = None、値がある場合はpresent: i64? = some(42)です。T?はOption[T]の短い表記です。値を取り出すには両方のcaseを書きます。
match present:
case Some(value):
print(value)
case None:
print("値なし")
使わない値はSome(_)にします。汎用unwrap APIはありません。空配列はvalues: List[i64] = []と型を指定してください。
関数の引数・戻り値・借用の書き方は文法、コピーとmoveの規則は所有権、各関数の対応する型は組み込み関数を参照してください。
Nagi 0.1.10以降では、view[bytes]をforで読むと、各要素はu8です。indexで取り出す値もu8になります。view[str]の文字単位の反復や整数indexは未対応です。文字列の一部を借りる場合は、UTF-8のbyte境界を確認するsliceを使います。
Nagi 0.1.10以降では、関数内で引数のviewを局所データのviewへ付け替えたり、viewを含む配列へ局所データのviewを追加したりできます。局所データを借りたviewは、そのデータの寿命を越えて保存・返却できません。Nagi 0.1.11への導入対象では、viewを含むList/Result/Optionの復元をnested value・branch・loop・asyncで検査するflow planと回帰例を追加しています。これは任意のview組合せすべての対応を意味しません。サポートされるNagiコードをcheckした後に生成RustがNagi由来の型・move・lifetime問題で拒否する場合はcompiler defectとして扱います。Rust adapterのcrate APIやtrait、target、依存環境は引き続き別途Rust buildで確認します。
このページの目次
enumで種類を分ける
enum Choice:
Cancelled
Selected(id: i64)
Choice.CancelledかChoice.Selected(id=42)を作り、matchで全種類を処理します。情報を持つ種類は位置引数でも作れます。全payloadがコピー可能ならenumもコピーできます。str・Error・Listなどを含む場合はmoveします。
classと同じく、ファイルをimportして使えます。enumの型引数・メソッド・JSON変換は未対応です。エラー処理にResultと組み合わせる例があります。
標準HTTPのresource型
std.http.serverからRequest、Response、Method、Status、Options、App[State, E]をimportできます。通常のclassと異なり、これらはlibraryが作るnative値です。名前付きfieldで直接構築したり、JSONへ変換したりはできません。コピーできるのはStatusだけです。Methodの一致比較は借用し、Requestのmethodを取り出す代入はmoveします。
view[Request]など、登録済みHTTP resourceのviewは値そのものへの参照です。通常のview[Point]は引き続きPointの配列のsliceで、Point1個への参照ではありません。Requestのbody・path・headerのviewは所有元を借り、所有元より長く保存できません。
resourceのviewにはlen・slice・index取得・forを使えません。List[Status]は直接index取得や反復ができますが、resourceのListからviewを作る操作は未対応です。
AppのhandlerはRequestをmoveで受け取り、stateはshared[State]で読みます。共有stateのCopy fieldは読み出せますが、strなどの非Copy fieldはmoveできません。view(state.label)で借用してください。Stateのfieldにはsqlite.Poolを含められますが、sqlite.PoolやHTTP resourceを含むclassはJSON変換に対応しません。AppのState・E型引数にはviewを保持できません。詳しくはHTTPと標準importを参照してください。
関数を値として渡す
関数名を変数へ代入したり、別の関数へ渡したりできます。fn[i64, i64]は「i64を1つ受け取り、i64を返す関数」です。引数がない場合は、戻り値の型だけを書きます。たとえばfn[i64]です。
次をapp.nagiに保存してください。
def add_one(value: i64) -> i64:
return value + 1
def apply(callback: fn[i64, i64], value: i64) -> i64:
return callback(value)
def main():
chosen: fn[i64, i64] = add_one
print(apply(chosen, 41))
nagic run app.nagi
結果は42です。chosen = add_oneのようにローカル変数の型を省略しても推論されます。関数を返す場合は、たとえばdef choose() -> fn[i64]:と宣言します。
fn[view[str], view[str]]のように借用を保持する引数がある場合、戻り値のviewは、引数で借りたデータが有効な間だけ使えます。借用を保持する引数がないfn[view[str]]やfn[i64, view[str]]では、プログラム終了まで有効なデータだけを借用で返せます。List[view[str]]やOption[view[str]]など、戻り値の中にviewがある場合も同じ規則です。
たとえば、HTTPステータスのphraseは静的な文字列です。次のコードはOKを出力します。
import std.http.server as http
def phrase() -> view[str]:
return http.Status.OK.phrase
def apply(factory: fn[view[str]]) -> view[str]:
return factory()
def main():
chosen = phrase
print(apply(chosen))
関数が所有する文字列のviewは返せません。静的なviewは、ローカル変数へ保存して返すこともできます。上の例では静的な文字列を借りて返すため、戻り値を渡すときに文字列を複製しません。
async関数もselected = answerのように代入して、async関数内でawait selected(...)と呼べます。HTTPのroute / route_mappedはこの名前付きasync関数やローカルaliasを登録できます。一般のasync関数を受け取る引数や返す関数の型注釈にはまだ対応していません。ラムダ式や、周囲のローカル変数を取り込むクロージャも未対応です。
async関数を配列やclassへ保存することも未対応です。非同期処理を呼び出した戻り値をいったん変数へ保存する形式も使えません。pending = sleep(10)ではなく、await sleep(10)と呼び出してください。
未対応の型操作
Map[K, V]は型表記を受け付けますが、Map専用の生成・読取り・更新APIはありません。利用者が型引数を持つ関数を定義する機能や、traitは未対応です。
owned[T]は未完成の型表記です。Rust生成時にはTになりますが、Nagiの型検査ではTと区別されます。tryやviewなども通常の型と一貫して扱えていません。所有文字列にはstr、所有配列にはList[T]をそのまま使ってください。Resultの検査範囲はエラー処理にあります。
Rustでの表現
数値とboolはRustの同じ型、strはString、bytesとListはVec、sharedはArcへ変換します。viewは参照、nullableはOption、成功・失敗はResult、enumはRustのenumです。詳しくはメモリの扱いを参照してください。
符号付き整数の最小値
i8の-128、i16の-32768、i32の-2147483648、i64の-9223372036854775808もリテラルとして直接書けます。たとえばminimum: i8 = -128です。指定した型の範囲を超えるリテラルは、型検査でエラーになります。