Rust言語一巡り

はじめに

 個人的に興味を持っているプログラミング言語Rustチュートリアルに一通り目を通した。Python/C++プログラマーが興味を持ったRustの機能を順不同で概説する。詳細は先の2つのリンク先を見てほしい。

cargoコマンド

 Rust言語はcargoコマンドで管理される。プロジェクトの作成とビルド、テスト、実行が以下のコマンドで行われる。

他にもプロジェクト管理に便利な機能をたくさんサポートしている。他の言語であれば、統合開発環境(IDE)が担いそうな機能である。ところで、なぜ「cargo」という名前にしたのだろう?

実行時のエントリーポイント

 実行時のエントリーポイントはいつもの関数である。

fnという潔い省略(関数を表す識別子)が魅力的だ。

デフォルトでimmutable

 Rustは型を自動推定する静的型付け言語である。変数はデフォルトでimmutable(定数)になる。

コード中に記載したように定数変数への代入はコンパイル時に検出されエラーとなる。変数を非定数とする場合は明示的にmutを書く必要がある(これはmutableのことである)。デフォルトで定数とするのは、関数型言語(例えばHaskell)の参照透過性を真似たものだろう。昨今のモダンな言語は関数型言語からの影響が大変大きい。

パターンマッチがある

 パターンマッチの例を以下に示す。

パターンマッチも関数型言語では当たり前の機能である。

参照による呼び出し

 関数を以下のように定義すると

引数は参照呼出しになる。

Rustの参照はC/C++のポインタに相当すると思えばよい。

上のように型を明示的に書くこともできる。

文字列型

 文字列型に2種類ある。1つは書き換えできない固定長の文字列を表す&str型、もう1つは書き換え可能なString型である。前者は言語に組み込まれた文字列型、後者は標準ライブラリとして提供される型である。これらの文字列型の文字コードはUTF-8である。

所有権の移譲

 代入を行うと所有権も移譲される。

所有権のないインスタンスに対する操作はコンパイル時に検出されエラーとなる。所有権の移譲はRust言語の一大特長である。また、immutableな変数の参照は何度でも取れるが、mutableな変数の参照は一度しか取れないルールがある。

8行目でmutable変数に対する2回目の参照を取っているので、zを使う9行目でエラーが検出される。もし、2つの参照に対する操作が許されると、同じメモリ領域に書き込む変数が2つ存在し、データ競合が起きるかもしれない。Rustはこれを未然に防ぐため上のルールを課している。

クラスはない

 C++のクラスに相当する機能はない。構造体に相当するものはある。

このとき以下のようにインスタンスを作る。

メンバ関数(に相当するもの)は構造体とは別に定義する。

関数の引数に&selfを書くとクラスメッソドになり、selfを通してメンバ変数にアクセスできる。関数の引数にselfを持たないものはstatic関数になる。Rustはコンストラクタを持たないので、もし必要であるなら上のnewのような任意の名前で、インスタンスを返す静的関数を定義することになる。呼び出し方は以下の通り。

1行目はインスタンスpからのメンバ関数の呼び出し、2行目と3行目は静的関数の呼び出しである。また、関数定義内の一番最後の行の末尾にセミコロンを書かないとその行は自動的に返り値になる。

C++のstd::optionalに相当する型

 std::optionalに相当する型がある。

自由度の高いenum

 C++より自由度の高いenumがある。

enumの要素として

  • 列挙子(Quit
  • 構造体(Move
  • タプル構造体(WriteChangeColor
  • を取ることができる。ひとつ前で紹介したstd::optionalライクな型は実はこのenumを用いて実装されている。

    テンプレートがある

     以下はテンプレート関数の定義である。

    テンプレート引数Tは、コンパイル時に実際の型に置き換えられる。これは、C++と同じ仕組みのテンプレートである。関数に渡した引数が関数内部で使われないときは上のように_と書くことができる。呼び出し側は以下の通り。

    ひとつ前で紹介したOptionの定義にもテンプレートは使われている。

    traitsがある

     C++にtraitsに相当するものはない。Javaのinterfaceに相当する機能である。Rustではtraitsを用いて振る舞いを継承することができる。例えば、まず最初に以下のCrytraitsを定義する。

    次に構造体を定義する。

    これらの構造体に対してCrytraitsを定義する。

    構造体Birdには意図的にcryを定義しないことにする。このとき、以下の呼出しができる。

    次に以下の関数を定義する。

    これはテンプレート関数であり、かつ、その型TCrytraitsを実装していなければならないこを要求する。これはC++のconceptに相当する仕組みである。このとき、以下のように書くことができる。

    BirdCrytraitsを実装していないのでコンパイル時にエラーになる。

    ポリモーフィズムができる

     以下のコードでポリモーフィズムができる。

    Boxは静的関数Box::newの引数に与えたインスタンスをヒープ領域に確保し、C++のRAIIと同じ仕組みでメモリを管理するクラスである。また、コード中のdynは仮想関数テーブルを用いた動的ディスパッチを行う際に必要となる呪文である(と書いたがこのdynは省略可能である)。C++のstd::shared_ptrに相当するクラス(Rc)もあるが今回は割愛する。

    ライフタイムがある

     以下の関数定義を考える。

    'aはライフタイムパラメータと呼ばれる。上の場合、引数の2つの変数と返り値の生存期間が同じであることをコンパイラに教えている。ライフタイムを明示することで、すでに寿命の尽きた変数を参照するバグをコンパイル時に検出できるようになる。以下のように使うことができる。

    まとめ

     最近チュートリアルを読んだRust言語を簡単に紹介した。巷ではC++を継ぐ言語と言われている。確かに、C++が嫉妬しそうな機能と構文がたくさんある。速度面でもC++と同等らしい。画像処理や機械学習のライブラリが充実してきたら実務で使いたい。私の場合、数値計算系のライブラリが充実しない限り最初の頃の熱意を持続できない。第2のD言語にならないことを祈る(笑)。

    Kumada Seiya

    Kumada Seiya

    仕事であろうとなかろうと勉強し続ける、その結果”中身”を知ったエンジニアになれる

    最近の記事

    • 関連記事
    • おすすめ記事
    • 特集記事

    アーカイブ

    カテゴリー

    PAGE TOP