Poss本記事は、原著者の許諾のもとに翻訳・掲載しております。programmersの流れに沿っています。この記事の最新版は下記サイトで見られます。PDFプロローグC言語プログラマのためのRustと、関数型プログラマのためのRustは異なります。他のプログラミング言語を使用した経験のあるC言語系統のプログラマなら、Rustはとても多くの新しい特徴と変わった構造を持っていると感じるでしょう。どちらかというと難解と感じるかもしれません。C++、JavaやC#プログラマでさえ、Rustは別物のように感じます。Rustはオブジェクトを持ちますがクラスを持ちません。enums”を持ち、C言語系統では同等のものがない変わったマッチステートメントを使います。同じ変数を2度割り当てること避け、C系統言語では聞いたことのない変則的なルールを適用しています。それはなぜでしょう?現在のRustマニュアルではあえて明言していないものの、Rustは関数型言語であり、ここ数年主流となっているプログラミング言語デザインの影響を受けています。Rust支持者が直面している問題があります。それは言語としても規律としても、関数型プログラミングが30年にわたり汚名を着せられてきたことです。関数型プログラムは判別が難しい、C言語に比べ処理が遅い、実行時には高くつく機構が必要になる(少なくともガベージコレクタ、しばしばシステムI/Oの関数型ラッパなど)、また学ぶのが難しく、変則的なシンタックスを使用するなどです。C言語プログラマへ関数型言語としてのRustを説明する場合、受け入れてもらうにはとても骨が折れるでしょう。いうのも公式のRustマニュアルとチュートリアルは明確に、下位タスクの実行に、どのようにRustを使用するか説明していますし、多くのCプログラミングパターンに相当するRustを現行のプログラミング言語に言及することなく、少しずつ説明していくとしています。これがすべてであると言えるのですが、もしあなたがすでに関数型プログラミングについて知っていたらどうでしょうか?Haskell、OCamlなどを使用したことのある経験者にとって、関数型プログラミングの基礎を事細かく説明したマニュアルを読むことはとても退屈なことでしょう。対象者のレベルに合わせ、以下では関数的側面から急ぎ足でRustの紹介をしていきます。Rustを学ぶべきなのか良くも悪くも、この先も大抵のハードウェアプロセッサはプログラムカウンタ、レジスタ、そしてアドレス指定のできるメモリに基づいていくだろうとされています。これは私たちが1970年代にいた場所とまったく変わっていません。当時はC言語が設計された時代であり、C言語が現在も普及している理由になっています。C言語の抽象機械モデルはほとんどのハードウェアのプラットフォーム上で適しているとされ、割り込みハンドラやガベージコレクタ、仮想メモリマネージャのような低レベルのシステムを構築するのにちょうどいいレベルの抽象化になっています。C言語の“ユーザインターフェース”、特にプリプロセッサと型システムは、とても時代遅れになっています。新しいものを学んだプログラマにとっては、率直に言ってダメな言語です。したことを背景にRustはつくられてきました。RustはC言語の抽象機械モデルを保ちますが、言語のインターフェースが刷新されています。Rustは書き方のバリエーションが豊富で、型システムはシステムコードをより安全にします。その強力なメタプログラミングファシリティはコードの自動作成の新しい方法を可能にします。この記事を書いている時点ではRustは、いまだ安定していないことに注意してください。ほとんど予告なく、まだ変更されることがあり、公式の資料は実装と完全には同期されていません。WordHaskellと同じように、Rustの数値リテラルでサフィックスがないものについでは、あらかじめ定義された型はありません。実際の型はコンテキストから推論されます。Rustの文字リテラルはどんなUnicodeのスカラ値も表すことができ、Latin-1エンコードのみのOCamlの文字とは対照的です。Rustの他のリテラルフォームは同記事内で後述するリテラルのセクションで示します。UTF-8エンコード済み文字列Rustではstr型は特別です。プリミティブなので、コンパイラは特定の文字列演算を最適化することができます。第一級ではないために、str型の型変数を定めたり、関数に直接str型の値を渡すことができません。プログラムでRustの文字列を使用するには、後で述べるように文字列参照を使用しなければいけません。-}Rustはブーリアン演算と、整数型上での単一ビットごとのNOT演算子の両方に!を使います。単一の~はRustでは、以下で説明するように、C言語とは違った意味を持ちます。ブロック式(unitで終わる)ブロック式の値は、ブロック内の最後の式の値になることに注意してください。ブロックがセミコロンで終わる場合は、その値は()になります。25明らかに、OCamlと同様に、Rustは正格(先行)評価を行い、関数が副作用のある式を含むことが可能です。Rustが用いるLLVMコードジェネレータは上記の関数に対して末尾呼び出しの除去ができますが、Rustは末尾呼び出しの除去を(現在のところ)保証していないことに注意してください。不確かな場合は、下記が同等です。n)トレイト:Rustの型クラスRustの“トレイト”はHaskellの型クラスに似ています。Haskellと主に違う点は、トレイトはドット表記法の式に対してのみ介在するということです。a.foo(b)のような形です。C++、Java、C#、OCamlのプログラマは、トレイトを伝統的なオブジェクトクラスと混同してはいけません。トレイトはまさに型クラスです。トレイトを、プリミティブ型を含む任意のデータ型に追加することができます。xトレイトのメソッド宣言において、識別子“self”は、そのメソッドが適用される実際のオブジェクトを示します。Haskellと同様に、Rustのトレイトは演算子のオーバーロードに使うことができます。Peanoそして、PartialEqクラスを例示することで、比較演算子==をPeano整数の間にオーバーロードできます。Falseまた、Haskellと同様に、トレイトはメソッドに対するデフォルト実装を提供できます。インスタンスが特殊化を省略する時に使用されます。y)トレイト宣言内のメソッド宣言において、識別子“self”はそのトレイトが適用される実際の型を指します。Rustにおいてオーバーロード可能な各演算子には、標準ライブラリの中に対応するトレイトがあります。cforループは次のように、特殊なトレイトstd::iter::Iteratorを使用します。}}メソッドnextはIteratorが実装します。nextの戻り値の型はOptionです。これは、“繰り返すものが残っていない”ことを意味する値None、あるいは次の繰り返し値がxであることを意味する値Some(x)を持つことができます。アドホックなオブジェクトとメソッドトレイトが提供する仕組みに加えて、どのstructやenumも、“impl”を使って1つ以上のメソッドインターフェースを使ってデコレートすることができます。デコレートは定義から分離され,異なるモジュール内ででデコレートすることができます。B.hello();}安全な参照C言語と同様に、Rustの関数パラメータはデフォルトで、値によって渡されます。大きなデータ型では、データのコピーはコストが高くなるかもしれないため、代わりに参照を使うことも考えられます。型Tのどんなオブジェクトvでも、式“&v”を使ってそのオブジェクトを指す参照を作ることができます。その参照自体はその時、型&Tを持つことになります。&b));}この例で示しているように、Rustはstruct参照のためのシンタックスシュガーを提供しています。p1の型が&PtでありPtがxという名前のフィールドを持つ場合は、p1.xと書くことも可能です。このシンタックスシュガーはメソッド呼び出し(x.foo())にも利用できます。他の多くのケースでは、参照される値が*単行演算子で“取得される”か、パターンが&でマッチしなければなりません。}}単純な参照では下部のオブジェクトの変更を許していません。このため、参照によって変更できることが望ましい場合は、次のように“&mut”を使用します。OK}Rustの型システムは、参照を通した可変な別名を許しません。C言語とは違って、参照で別名を使って同じオブジェクトを変更することはできません。これは借用という概念を通して行われます。オブジェクトの所有権が参照によって借用されている間は、元の変数を使用できないということです。3)}“すべての値はデフォルトでは不変”であることや所有権・借用のルールと共に、参照型はRustの型システムの核となる特徴です。この特徴によって、Rustの型システムはC言語よりも根本的に安全なものとなっています。本セクションで示す概念とシンタックスは、Rustのバージョン0.11で追加されたものです。0.10以前のバージョンは異なる概念、用語、シンタックスを使用しています。この記事の執筆時点(2014年7月)において、公式のRustマニュアルとチュートリアルは最新の言語実装のものにまだ更新されていません。本セクションはマニュアルではなく実装に即しています。はじめにRustはC言語と同じ抽象機械モデルについて定義されています。この抽象機械にはメモリのセグメントがあり、言語のランタイム機構はプログラムの実行中にメモリのセグメントの割り当てと解除を行うことができます。抽象機械において、RustではC言語と同様に下記の2つの概念が定義されています。オブジェクトの記憶域によって、そのオブジェクトが格納されるメモリの型が決まります。オブジェクトの寿命つまり記憶期間は、オブジェクトが割り当てられた点から解除された点までの時間のセグメントです。C言語におけるメモリ関連の問題はすべて、Cプログラムでは寿命外(すなわち割り当て前または解除後)あるいは記憶域外(すなわちメモリの下位アドレスまたは上位アドレス)のオブジェクトを指す参照を操作できることから起こっています。Rustは、オブジェクトが寿命外または記憶域外で使用できないことを確実にすることで、そうした問題を徹底的に防ぐようにしています。Rustの記憶域とC言語の記憶域C言語やRustの抽象機械には、4種類の記憶域、つまりstatic(静的)、thread(スレッド)、automatic(自動)、allocated(割り当て)があります。i;C言語では、オブジェクトの寿命はその記憶域だけで決まります。オブジェクト割り当てからスレッド終了までRustでは、staticとautomaticのオブジェクトの寿命はC言語と同じです。Rustは新たに“Box”型を導入しています。ヒープに割り当てられたオブジェクトのための専用の構文をもち、そのオブジェクトは管理オブジェクトと呼ばれます。Rustは、さまざまな型付けルールに関連して、ボックスに対する多くの管理戦略をサポートしています。デフォルトのボックス管理戦略は、ボックスの所有者が1人だけであることを保証します。すればコンパイラは、いつボックスの寿命が終わるかを正確に把握でき、参照カウントやガベージコレクションなどのようにシステムに余計な負荷をかけることなく、割り当てを安全に解除することができます。1つの戦略は、遅延ガベージコレクションを使うGCです。Rustのランタイムシステムが不要だと決めた時点で、GCボックス用の記憶域が再生され再利用が可能になります(再生には想定外の時間がかかるかもしれません)。GCボックスへの参照は1つだけ(ユニーク)である必要はありません。GCボックスは、グラフのような共通のノードを持った複雑なデータ構造を構築する上で適切な型です。C言語とは違い、Rustはスレッド間での管理オブジェクトの共有を許しません。Erlangと同様に、オブジェクトは割り当て毎に1人の所有者を持ちます。ほとんどのオブジェクトは、それぞれのスレッドだけで使われます。これによって、大抵の場合はデータの競合を防ぐことができます。1つのスレッドに所有権を限定することにより、GCオブジェクトのガベージコレクションをスレッド間で同期する必要がありません。実装が簡単で、動作を速くできます。通常のオブジェクトへの参照と同様に、管理オブジェクトを参照している可変な別名をRustは許しません。ベクターへのアクセスはすべてチェックされ、参照はポインタの演算をサポートしません。Rustは管理されていないポインタを使用しないよう強く推奨しています。安全ではない特性があるとして、システムでコンパイラに警告を誘発させます。ボックスの作成と使用Rustの管理オブジェクトの扱い方は、比較的シンプルです。boxes”の式にあるboxキーワードによって、オブジェクトの寿命が動的に管理されます。ボックスの不可変性と所有権については、前述した参照と同様の考え方です。[42,2,3]}boxキーワードは、実際にはbox(HEAP)の省略形です。E”は、Aによって割り当てられたメモリオブジェクトにEの値の結果を置くことを意味し、これがトレイトです。Rustのバージョン0.11において、これ以外に標準ライブラリ内にあるアロケータは、ガベージコレクションされたオブジェクトであるGCだけです。“v”のコピーへのユニークな参照。スマートポインタの一種。lオブジェクトの共有:RcとArcボックスが提供する利便性を補完するものとして、Rustの標準ライブラリには、参照がカウントされる不変オブジェクトへのラッパーが2つ実装されています。これにより複数のオーナーからの参照が可能になります。OK}参照カウントを使えば、最後の参照が終了したその時に、オブジェクトの割り当てが解除されたことが保証されます。ボックスの使用のトレードオフは、新しい参照が作られたり参照が終わったりするたびに、参照カウントを更新しなければならない、ということです。std::rc::Rcとstd::arc::Arcという2つの実装が提供されています。どちらも同じインターフェースを提供します。同じようなものがあるかというと、プログラマにパフォーマンス上のトレードオフを与えるためです。Rcはメモリを使いません。軽量で、つまり速いのです。スレッド間で共有できません。Arcはメモリを使います。Rcと比べるとやや効率が悪いですが、スレッド間でデータを共有できます。マクロとメタプログラミングマクロを定義する基本的なシンタックスは次のようになります。...)例えば、次に示すマクロは、forループをPascalのように定義したものです。});}このマクロが、ローカルネームを付けるに当たってどのようにマッチステートメントを使っているかに注目してください。これによって1度しか値を返さないようになっています。Scheme同様、マクロも再帰可能です。次に示すマクロでは、stepがあってもなくてもpforを実装するために再帰を使っています。});}マクロは可変個引数であり、シンタックス内の任意の繰り返しは、マクロの1つの引数で記録できます。次に示すマクロでは、各引数においてprintln!を呼び出し、それは任意の型になり得ます。3.14);}シンタックスは次のようになります。)DELIM*が、DELIMによって区切られたTEXTの1回またはそれ以上の繰り返しを展開します。展開される繰り返しの回数は、含まれるマクロの引数がマッチする回数によって決まります。示した例の中では、それぞれの引数(カンマによって区切られる)は、セミコロンによって区切られるprintln!という呼び出しで代用されています。リテラルRustにはさまざまな数値リテラルの語彙があります。"\U00012345"C言語ファミリでよく使われるエスケープ(\a、\fなど)や、8進法表記のエスケープ(例、\0123)は、Rustでは使えないことに注意してください。最後に、RustはPython同様に、raw文字列、複数の文字列のデリミタをサポートしており、これにより文字列内のデリミタのオカレンスを引用することを避けています。Newsでクオリティの高いコメントを寄せてくれた多くの人たちに感謝します。この記事の初版に磨きをかけるに当たって、いただいたコメントが大変参考になりました。http://creativecommons.org/licenses/by-sa/4.0/.ツイート翻訳に対するフィードバックがございましたら、メールまたはGitHubのIssueよりお寄せください。Ltd.リクルートグループサイトへ。