Rust by Example 日本語版

Rust は安全性、速度、並列性にフォーカスした現代的なシステムプログラミング 用のプログラミング言語です。 ガベージコレクション無しでメモリ安全であることが、これを可能にしています。

Rust by Example(RBE)はRustの実行可能なサンプルスクリプト集で、ここではRustの様々な コンセプトと標準ライブラリを紹介していきます。 この例をより活用するためにはRustをローカルにインストールし、公式ドキュメントをチェックすることをおすすめします。 興味がある方はこのサイト自体のソースのチェックもどうぞ。

訳注: 日本語版のソースコードはこちらにあります。

それでははじめましょう!

  • Hello World - お決まりのHello Worldプログラムから始めましょう。
  • 基本データ型 - 符号付き整数や符号無し整数、その他の基本データ型について学びましょう。
  • 変数の束縛 - ミュータブルな束縛、スコープ、シャドーイングについて。
  • - 型を変更したり定義したりすることを学びましょう。
  • 関数 - メソッド、クロージャ、高階関数について。
  • モジュール - プログラムをモジュールを使って整理しましょう。
  • クレート - クレートは、Rustにおいてコンパイルされる単位です。ライブラリの作り方について学びます。
  • Cargo - Rustの公式パッケージマネージャの基本的な機能を学びます。
  • アトリビュート - アトリビュートは、モジュールやクレート、要素に適用されるメタデータです。
  • ジェネリクス - 様々な型の引数を取れる関数やデータ型を書く方法を学びましょう。
  • スコープの規則 - スコープは所有権、借用、ライフタイムにおいて重要な役割を果たします。
  • トレイト - トレイトとは、未知の型Selfに対して定義された一連のメソッドです。
  • テスト - Rustにおけるテストのすべて。
  • 周辺情報 - ドキュメント、ベンチマークの方法。

Hello World

ここでは伝統的な"Hello World!"プログラムのソースを紹介します。 

// This is a comment, and is ignored by the compiler
// You can test this code by clicking the "Run" button over there ->
// or if you prefer to use your keyboard, you can use the "Ctrl + Enter" shortcut
// これはコメントです。コンパイラによって無視されます。
// 右にある「Run」ボタンからこのコードをテストできます。
// キーボードを使いたければ「Ctrl + Enter」もOKです。

// This code is editable, feel free to hack it!
// You can always return to the original code by clicking the "Reset" button ->
// このコードは編集可能です。ぜひハックしてみましょう!
// 「Reset」ボタンでいつでも元のコードに戻すことができます ->

// This is the main function
// main関数です
fn main() {
    // Statements here are executed when the compiled binary is called
    // コンパイルされたバイナリが実行されるとこの関数が呼び出されます

    // Print text to the console
    // コンソールに文字列を出力する
    println!("Hello World!");
}

println!は文字列をコンソールにプリントするための マクロ です。

バイナリファイルはrustcと呼ばれるRustのコンパイラを用いて生成することができます。

$ rustc hello.rs

するとhelloという名前の実行可能なバイナリファイルができます。

$ ./hello
Hello World!

演習

上に書いている'Run'をクリックしてアウトプットを見てみましょう。 次に、println!マクロをもう一行追加してアウトプットがどうなるか見てみましょう。

Hello World!
I'm a Rustacean!

コメント

あらゆるプログラムにはコメントが必要です。Rustには何種類かのコメントがあります

  • 通常のコメント これはコンパイラによって完全に無視されます。
    • // 行末までコメントアウト
    • /* ブロックによって囲まれた部分をコメントアウト */
  • ドキュメンテーションコメント ライブラリのドキュメンテーションとしてhtmlにパースされます。
    • /// このコメントの下の内容に関するドキュメントとなります
    • //! このコメントを含むソースのドキュメントになります
fn main() {
    // This is an example of a line comment
    // There are two slashes at the beginning of the line
    // And nothing written inside these will be read by the compiler
    // こちらはラインコメントです
    // 一番左にスラッシュが2つある行と、何も書かれていない行は
    // どちらもコンパイラによって無視されます。試しに実行してみてください

    // println!("Hello, world!");

    // Run it. See? Now try deleting the two slashes, and run it again.
    // でしょ?では次に、左のスラッシュを消去してから実行してください

    /* 
     * This is another type of comment, a block comment. In general,
     * line comments are the recommended comment style. But
     * block comments are extremely useful for temporarily disabling
     * chunks of code. /* Block comments can be /* nested, */ */
     * so it takes only a few keystrokes to comment out everything
     * in this main() function. /*/*/* Try it yourself! */*/*/
     */
    /*
     * こちらはもう一つのタイプのコメントでブロックコメントと呼ばれます。
     * 普通はラインコメントの方が優れているのですが、こちらはデバッグ時に
     * 役立つ場合があります。
     */

    /*
    Note: The previous column of `*` was entirely for style. There's
    no actual need for it.
    */
    /*
    このように、`*`は、実際にはコメントの前後に1つずつあれば十分です。
    */

    // You can manipulate expressions more easily with block comments
    // than with line comments. Try deleting the comment delimiters
    // to change the result:
    // ではブロックコメントがどのようにデバッグに役立つか見てみましょう。
    // 例えば下の例の場合、ブロックコメントがなくなれば結果が変わります。
    let x = 5 + /* 90 + */ 5;
    println!("Is `x` 10 or 100? x = {}", x);
}

参照

ライブラリドキュメンテーション

フォーマットしてプリント

プリント関係の機能はstd::fmtで定義される幾つかのマクロによって扱われます。このマクロには以下が含まれます。

  • format!: フォーマットされたテキストを文字列(String)型に書き込みます。
  • print!: format! と同様ですが、コンソール (io::stdout) にそのテキストを出力します。
  • println!: print!: と同じですが改行が付け加えられます。
  • eprint!: format! と同様ですが、標準エラー出力 (io::stderr) にそのテキストを出力します。
  • eprintln!: eprint!と同じですが改行が付け加えられます。

すべて同じやり方でテキストをパースし、正しくフォーマットできるかコンパイル時にチェックします。

fn main() {
    // In general, the `{}` will be automatically replaced with any
    // arguments. These will be stringified.
    // 一般的に `{} `はどんな引数であろうと自動的に置き換えられます。
    // 例えば以下は文字列に変換されます
    println!("{} days", 31);

    // Without a suffix, 31 becomes an i32. You can change what type 31 is
    // by providing a suffix. The number 31i64 for example has the type i64.
    // サフィックスで型を指定しなければ31はi32として扱われます。
    // サフィックスの指定により、31の型を自由に変換することができます。

    // There are various optional patterns this works with. Positional
    // arguments can be used.
    // 引数の位置から埋め込まれる場所を指定することができます。
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

    // As can named arguments.
    // 名前での指定も可能です。
    println!("{subject} {verb} {object}",
             object="the lazy dog",
             subject="the quick brown fox",
             verb="jumps over");

    // Special formatting can be specified after a `:`.
    // `:` のあとにフォーマット型を指定することによる特殊なフォーマットも可能です.
    println!("{} of {:b} people know binary, the other half doesn't", 1, 2);

    // You can right-align text with a specified width. This will output
    // "     1". 5 white spaces and a "1".
    // 指定した幅の中に、右寄せで文字列を挿入することができます。
    // 以下の例では"     1". というように、5つの半角空白のあとに"1"が入ります.
    println!("{number:>width$}", number=1, width=6);

    // You can pad numbers with extra zeroes. This will output "000001".
    // 空白の代わりに0を使うこともできます. このアウトプットは "000001" になります.
    println!("{number:0>width$}", number=1, width=6);

    // Rust even checks to make sure the correct number of arguments are
    // used.
    // 引数の数が正しいかのチェックも行ってくれます。
    println!("My name is {0}, {1} {0}", "Bond");
    // FIXME ^ Add the missing argument: "James"

    // Create a structure named `Structure` which contains an `i32`.
    // `i32`保持する `Structure` という名の構造体を定義します.
    #[allow(dead_code)]
    struct Structure(i32);

    // However, custom types such as this structure require more complicated
    // handling. This will not work.
    // このようにカスタム型を用いる場合、少々扱いが複雑になります。
    // 以下は動作しません。
    println!("This struct `{}` won't print...", Structure(3));
    // FIXME ^ Comment out this line.
}

std::fmtはいくつものトレイトを持ち、それによってどのようにディスプレイに表示されるかが決まります。 特に大事な形式は以下の2つです。

  • fmt::Debug: は、{:?}というマーカーを使用し、デバッギング目的に使われます。
  • fmt::Display: は {}というマーカーを使用し、より美しく、ユーザフレンドリーに表示します。

この例で用いられている型は、標準ライブラリに含まれているため、ここではfmt::Displayを使用しています。カスタム型をテキストとして表示する場合は、さらに手順が必要です。

fmt::Displayトレイトを実装すると、自動的にToStringトレイトが実装されます。これによりString型への型変換ができるようになります。

演習

  • 上の例を実行した際に生じるエラーを修復しましょう。
  • println!マクロを追加し、表示される小数部の桁数を調整してPi is roughly 3.142という文字列を出力しましょう。 ただし、円周率の値はlet pi = 3.141592を使ってください。(ヒント: 小数部の桁数を調整する方法については、std::fmtをチェックする必要があるかもしれません。)

参照

std::fmt, マクロ, 構造体, トレイト

デバッグ

std::fmtのフォーマット用トレイトを使用したい型は、プリント可能である用に実装されている必要があります。stdライブラリの型のように自動でプリント可能なものもありますが、他はすべて 手動で実装する必要があります。

fmt::Debugというトレイトはこれを簡略化します。 すべての 型はfmt::Debugの実装をderive、(すなわち自動で作成)することができるためです。 fmt::Displayの場合はやはり手動で実装しなくてはなりません。


#![allow(unused)]
fn main() {
// This structure cannot be printed either with `fmt::Display` or
// with `fmt::Debug`.
// この構造体は`fmt::Display`、`fmt::Debug`のいずれによっても
// プリントすることができません。
struct UnPrintable(i32);

// The `derive` attribute automatically creates the implementation
// required to make this `struct` printable with `fmt::Debug`.
// `derive`アトリビュートは、
// この構造体を`fmt::Debug`でプリントするための実装を自動で提供します。
#[derive(Debug)]
struct DebugPrintable(i32);
}

stdライブラリの型の場合は、自動的に{:?}によりプリント可能になっています。

// Derive the `fmt::Debug` implementation for `Structure`. `Structure`
// is a structure which contains a single `i32`.
// `Structure`という構造体のための`fmt::Debug`をderiveしています。
// `Structure`は単一の`i32`をメンバに持っています。
#[derive(Debug)]
struct Structure(i32);

// Put a `Structure` inside of the structure `Deep`. Make it printable
// also.
// `Deep`という構造体の中に`Structure`を入れます。
// また、これをプリント可能にしています。
#[derive(Debug)]
struct Deep(Structure);

fn main() {
    // Printing with `{:?}` is similar to with `{}`.
    // `{:?}`によるプリントは `{}`に似ています。
    println!("{:?} months in a year.", 12);
    println!("{1:?} {0:?} is the {actor:?} name.",
             "Slater",
             "Christian",
             actor="actor's");

    // `Structure` is printable!
    // `Structure`はプリント可能です!
    println!("Now {:?} will print!", Structure(3));
    
    // The problem with `derive` is there is no control over how
    // the results look. What if I want this to just show a `7`?
    // `derive`を用いることの問題は、結果がどのように見えるか
    // コントロールする方法がないことです。
    // 出力を`7`だけにするためにはどうしたらよいでしょう?
    println!("Now {:?} will print!", Deep(Structure(7)));
}

fmt::Debugは確実にプリント可能にしてくれるのですが、一方である種の美しさを犠牲にしています。 Rustは{:#?}による「見栄えの良いプリント」も提供します。

#[derive(Debug)]
struct Person<'a> {
    name: &'a str,
    age: u8
}

fn main() {
    let name = "Peter";
    let age = 27;
    let peter = Person { name, age };

    // Pretty print
    println!("{:#?}", peter);
}

手動でfmt::Displayを実装することでプリント結果を思い通りにできます。

参照

アトリビュート, derive, std::fmt, 構造体

ディスプレイ

fmt::Debugはコンパクトでクリーンであるようには見えませんね。大抵の場合は、アウトプットの見た目をカスタマイズしたほうが好ましいでしょう。これは{}を使用するfmt::Displayを手動で実装することで可能です。


#![allow(unused)]
fn main() {
// Import (via `use`) the `fmt` module to make it available.
// (`use`を使用し、)`fmt`モジュールをインポートします。
use std::fmt;

// Define a structure for which `fmt::Display` will be implemented. This is
// a tuple struct named `Structure` that contains an `i32`.
// `fmt::Display`を実装するための構造体を定義します。
// これは`Structure`という名前に紐付けられた、`i32`を含むタプルです。
struct Structure(i32);

// To use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
// `{}` というマーカーを使用するためには、
// この型専用の`fmt::Display`というトレイトが実装されていなくてはなりません。
impl fmt::Display for Structure {
    // This trait requires `fmt` with this exact signature.
    // このトレイトは`fmt`が想定通りのシグネチャであることを要求します。
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Write strictly the first element into the supplied output
        // stream: `f`. Returns `fmt::Result` which indicates whether the
        // operation succeeded or failed. Note that `write!` uses syntax which
        // is very similar to `println!`.
        // 厳密に最初の要素を、与えられた出力ストリーム `f` に書き込みます。
        // `fmt::Result`を返します。これはオペレーションが成功したか否か
        // を表します。
        // `write!`は`println!`に非常によく似た文法を使用していることに注目。
        write!(f, "{}", self.0)
    }
}
}

fmt::Displayfmt::Debugより綺麗かもしれませんが、stdライブラリの場合は問題が生じます。曖昧な(ambiguous)タイプはどのように表示すれば良いでしょう? 例えば、stdライブラリがあらゆるVec<T>に対して単一のスタイルを提供していた場合、どのようなスタイルに整形すればよいでしょう?以下の2つのどちらかを選ぶべきでしょうか?

  • Vec<path>: /:/etc:/home/username:/bin:で分割)
  • Vec<number>: 1,2,3,で分割)

答えはNOです。あらゆる型に対して理想的なスタイルなどというものはありませんし、stdライブラリによってそれが提供されているわけでもありません。fmt::DisplayVec<T>のようなジェネリックなコンテナ用に定義されているわけではありませんので、このような場合はfmt::Debugを使用するべきです。

ジェネリック でない コンテナ型の場合は、このような問題は生じませんので問題なくfmt::Displayを実装することができます。

use std::fmt; // Import `fmt`

// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
// 2つの数字を扱うための構造体です。出力を`Display`と比較するため`Debug`
// をDeriveしています
#[derive(Debug)]
struct MinMax(i64, i64);

// Implement `Display` for `MinMax`.
// `MinMax`用の`Display`を実装しています。
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// Define a structure where the fields are nameable for comparison.
// 比較のため、フィールドに名前をつけれる様な構造体を定義しましょう
#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64,
}

// Similarly, implement `Display` for `Point2D`
// 先程と同様にして、Point2D用の`Display`を実装しています。
impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Customize so only `x` and `y` are denoted.
        // `x`と`y`のみが明示的になるようにカスタマイズ
        write!(f, "x: {}, y: {}", self.x, self.y)
    }
}

fn main() {
    let minmax = MinMax(0, 14);

    println!("Compare structures:");
    println!("Display: {}", minmax);
    println!("Debug: {:?}", minmax);

    let big_range =   MinMax(-300, 300);
    let small_range = MinMax(-3, 3);

    println!("The big range is {big} and the small is {small}",
             small = small_range,
             big = big_range);

    let point = Point2D { x: 3.3, y: 7.2 };

    println!("Compare points:");
    println!("Display: {}", point);
    println!("Debug: {:?}", point);

    // Error. Both `Debug` and `Display` were implemented, but `{:b}`
    // requires `fmt::Binary` to be implemented. This will not work.
    // `Debug`と`Display`は実装されていますが、`fmt::Binary`はされていないため
    // `{:b}`使用している以下の例はエラーになります、
    // println!("What does Point2D look like in binary: {:b}?", point);
}

fmt::Displayは実装されていますが、fmt::Binaryはされていないので使用できません。 std::fmtはそのようなトレイトが数多くあり、それぞれに独自の実装が必要です。詳しくはstd::fmtを参照してください。

演習

上記の例のアウトプットを確認し、Point2D構造体を参考として、複素数を格納するためのComplex構造体を定義しましょう。うまく行けば以下のように出力されるはずです。

Display: 3.3 + 7.2i
Debug: Complex { real: 3.3, imag: 7.2 }

参照

derive, std::fmt, マクロ, struct, trait, use

テストケース: リスト

構造体のそれぞれの要素を別々に扱うfmt::Displayを実装するのはトリッキーです。というのも、それぞれのwrite!が別々のfmt::Resultを生成するためです。適切に処理するためには すべての resultに対して処理を書かなくてはなりません。このような場合は?演算子を使用するのが適当です。

以下のように?write!に対して使用します。

// Try `write!` to see if it errors. If it errors, return
// the error. Otherwise continue.
// `write!`を実行し、エラーが生じた場合はerrorを返す。そうでなければ実行を継続する。
write!(f, "{}", value)?;

Alternatively, you can also use the try! macro, which works the same way. This is a bit more verbose and no longer recommended, but you may still see it in older Rust code. Using try! looks like this:

try!(write!(f, "{}", value));

?を使用できれば、Vec用のfmt::Displayはより簡単に実装できます。

use std::fmt; // Import the `fmt` module.

// Define a structure named `List` containing a `Vec`.
// `Vec`を含む`List`という名の構造体を定義
struct List(Vec<i32>);

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Extract the value using tuple indexing,
        // and create a reference to `vec`.
        let vec = &self.0;

        write!(f, "[")?;

        // Iterate over `v` in `vec` while enumerating the iteration
        // count in `count`.
        // `v`を介して`vec`をイテレーションし、同時にカウントを
        // `enumerate`で取得する
        for (count, v) in vec.iter().enumerate() {
            // For every element except the first, add a comma.
            // Use the ? operator to return on errors.
            if count != 0 { write!(f, ", ")?; }
            write!(f, "{}", v)?;
        }

        // Close the opened bracket and return a fmt::Result value.
        // 開きっぱなしのブラケットを閉じて、`fmt::Result`の値を返す。
        write!(f, "]")
    }
}

fn main() {
    let v = List(vec![1, 2, 3]);
    println!("{}", v);
}

演習

上記のプログラムを変更して、ベクタの各要素のインデックスも表示するようにしてみましょう。変更後の出力は次のようになります。

[0: 1, 1: 2, 2: 3]

参照

for, ref, Result, 構造体, ?, vec!

フォーマット

これまで、文字列がどのようにフォーマットされるかは フォーマット文字列 によって決まるということを見てきました 。

  • format!("{}", foo) -> "3735928559"
  • format!("0x{:X}", foo) -> "0xDEADBEEF"
  • format!("0o{:o}", foo) -> "0o33653337357"

ここでは(foo)という単一の変数がXo指定なし 、という様々な 引数タイプ (argument type)に応じてフォーマットされています。

フォーマットの機能はそれぞれの引数タイプごとに個別のトレイトを用いて実装されています。 最も一般的なトレイトはDisplayで、これは引数タイプが未指定(たとえば{})の時に呼び出されます。

use std::fmt::{self, Formatter, Display};

struct City {
    name: &'static str,
    // Latitude
    // 緯度
    lat: f32,
    // Longitude
    // 経度
    lon: f32,
}

impl Display for City {
    // `f` is a buffer, and this method must write the formatted string into it
    // `f` はバッファです。このメソッドは
    // ここにフォーマットされた文字列を書き込みます。
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
        let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };

        // `write!` is like `format!`, but it will write the formatted string
        // into a buffer (the first argument)
        // `write!`は`format!`に似ていますが、フォーマットされた文字列を
        // バッファ(第一引数)に書き込みます。
        write!(f, "{}: {:.3}°{} {:.3}°{}",
               self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c)
    }
}

#[derive(Debug)]
struct Color {
    red: u8,
    green: u8,
    blue: u8,
}

fn main() {
    for city in [
        City { name: "Dublin", lat: 53.347778, lon: -6.259722 },
        City { name: "Oslo", lat: 59.95, lon: 10.75 },
        City { name: "Vancouver", lat: 49.25, lon: -123.1 },
    ].iter() {
        println!("{}", *city);
    }
    for color in [
        Color { red: 128, green: 255, blue: 90 },
        Color { red: 0, green: 3, blue: 254 },
        Color { red: 0, green: 0, blue: 0 },
    ].iter() {
        // Switch this to use {} once you've added an implementation
        // for fmt::Display.
        // fmt::Displayに実装を追加したら、 {} を使用するように変更してください。
        println!("{:?}", *color);
    }
}

フォーマット用トレイトの全リスト、及び引数の型はこちらから、引数の型についてはstd::fmtのドキュメンテーションから参照できます。

演習

上にあるソースコード中のColorという構造体のためのfmt::Displayトレイトの実装を追加しましょう。アウトプットは以下のように表示されるはずです。

RGB (128, 255, 90) 0x80FF5A
RGB (0, 3, 254) 0x0003FE
RGB (0, 0, 0) 0x000000

詰まったら以下の2つがヒントになります。

参照

std::fmt

基本データ型

Rustは様々な基本データ型(primitives)の使用をサポートしています。以下がその例です。

スカラー型

  • 符号付き整数: i8, i16, i32, i64, i128, isize(ポインタのサイズ)
  • 符号無し整数: u8, u16, u32, u64, u128, usize(ポインタのサイズ)
  • 浮動小数点数: f32, f64
  • char: 'a', 'α', '∞'などのUnicodeのスカラー値
  • bool: trueまたはfalse
  • ユニット型: ()が唯一の値

ユニット型はその値がタプルですが、複合型とはみなされません。内部に複数の値を含んでいるわけではないからです。

複合型

  • 配列: e.g. [1, 2, 3]など
  • タプル: e.g. (1, true)

変数は常に 型指定(type annotate)可能 です。数値型の場合はさらにサフィックスでの指定が可能です。指定しない場合デフォルトになります。例えば整数はi32が、浮動小数点はf64がデフォルトです。また、Rustは文脈から型を推定することもできます。

fn main() {
    // Variables can be type annotated.
    // 変数に型を指定
    let logical: bool = true;

    let a_float: f64 = 1.0;  // Regular annotation
                             // 通常の型指定
    let an_integer   = 5i32; // Suffix annotation
                             // サフィックスによる型指定

    // Or a default will be used.
    // サフィックスを指定しない場合、デフォルトを選択
    let default_float   = 3.0; // `f64`
    let default_integer = 7;   // `i32`
    
    // A type can also be inferred from context 
    // 型を文脈から推定することも可能
    let mut inferred_type = 12; // Type i64 is inferred from another line
                                // 型 i64 は次行の内容に基づいて推定
    inferred_type = 4294967296i64;
    
    // A mutable variable's value can be changed.
    // ミュータブルな変数は値を変更できる
    let mut mutable = 12; // Mutable `i32`
                          // ミュータブルな `i32`.
    mutable = 21;
    
    // Error! The type of a variable can't be changed.
    // エラー! ミュータブルな変数でも型は不変
    mutable = true;
    
    // Variables can be overwritten with shadowing.
    // 変数はシャドーイングによって上書きできる
    let mutable = true;
}

参照

std ライブラリ, mut, inference, shadowing

リテラルとオペレータ

整数1、浮動小数点1.2、文字(char)'a'、文字列"abc"、ブーリアンtrue、ユニット()は、リテラルを使用することで明示することが可能です。

また整数型の場合、リテラルの代わりにプレフィックスに0x0o0bを指定することでそれぞれ16進数、8進数、2進数を使うことができます。

可読性のため、_(アンダースコア)を数値リテラルの間に挿入することができます。例えば1_0001000と、0.000_0010.000001とそれぞれ同一です。

コンパイラに、どのリテラルを使用するのかを教えてあげなくてはなりません。現在の仕様では、リテラルが32ビット符号無し整数であることを伝える場合、u32サフィックスを、符号付き32ビット整数であればi32を使用します。

Rustで使用可能な演算子と、その実行順序は、Cなどの言語のものとほぼ同じです。

fn main() {
    // Integer addition
    // 整数の足し算
    println!("1 + 2 = {}", 1u32 + 2);

    // Integer subtraction
    // 整数の引き算
    println!("1 - 2 = {}", 1i32 - 2);
    // TODO ^ Try changing `1i32` to `1u32` to see why the type is important
    // TODO ^ 型が重要であることを実感するため`1i32`を`1u32`に変更してみましょう。

    // Short-circuiting boolean logic
    // 単純な論理演算子
    println!("true AND false is {}", true && false);
    println!("true OR false is {}", true || false);
    println!("NOT true is {}", !true);

    // Bitwise operations
    // ビットワイズ演算
    println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
    println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
    println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
    println!("1 << 5 is {}", 1u32 << 5);
    println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);

    // Use underscores to improve readability!
    // 可読性のための`_`(アンダースコア)の使用
    println!("One million is written as {}", 1_000_000u32);
}

タプル

タプルは異なる型の値の集合です。括弧()を用いて生成します。タプル自体がそのメンバに対する型シグネチャを保持していますので、明示すると(T1, T2, ...)のようになります。タプルは大きさに制限がありませんので、関数が複数の値を返したい時に使われます。

// Tuples can be used as function arguments and as return values
// タプルを関数の引数及び返り値として使用している。
fn reverse(pair: (i32, bool)) -> (bool, i32) {
    // `let` can be used to bind the members of a tuple to variables
    // `let`でタプルの中の値を別の変数に束縛することができる。
    let (integer, boolean) = pair;

    (boolean, integer)
}

// The following struct is for the activity.
// 以下の構造体は後ほど「演習」で用いる。
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);

fn main() {
    // A tuple with a bunch of different types
    // 様々な型を値に持つタプル
    let long_tuple = (1u8, 2u16, 3u32, 4u64,
                      -1i8, -2i16, -3i32, -4i64,
                      0.1f32, 0.2f64,
                      'a', true);

    // Values can be extracted from the tuple using tuple indexing
    // インデックスを用いて、タプル内の要素を参照できる。
    println!("long tuple first value: {}", long_tuple.0);
    println!("long tuple second value: {}", long_tuple.1);

    // Tuples can be tuple members
    // タプルはタプルのメンバになれる
    let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);

    // Tuples are printable
    // タプルはプリント可能である。
    println!("tuple of tuples: {:?}", tuple_of_tuples);
    
    // But long Tuples cannot be printed
    // しかし長すぎるタプルはプリントできない
    // let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
    // println!("too long tuple: {:?}", too_long_tuple);
    // TODO ^ Uncomment the above 2 lines to see the compiler error
    // TODO ^ 上記2行のコメントを外して、コンパイルエラーになることを確認

    let pair = (1, true);
    println!("pair is {:?}", pair);

    println!("the reversed pair is {:?}", reverse(pair));

    // To create one element tuples, the comma is required to tell them apart
    // from a literal surrounded by parentheses
    // 要素を1つしか持たないタプルを作成する場合、括弧で囲まれたただのリテラル
    // と区別するため、カンマが必要になる。
    println!("one element tuple: {:?}", (5u32,));
    println!("just an integer: {:?}", (5u32));

    //tuples can be destructured to create bindings
    //タプルを分解して別の変数にそれぞれの値を代入
    let tuple = (1, "hello", 4.5, true);

    let (a, b, c, d) = tuple;
    println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d);

    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
    println!("{:?}", matrix);

}

演習

  1. 復習 : 上にあるMatrixという構造体に、fmt::Displayトレイトを追加しましょう。デバッグフォーマット{:?}ではなくディスプレイフォーマット{}でプリントすることができるようになるはずです。

    ( 1.1 1.2 )
    ( 2.1 2.2 )
    

必要に応じてprint displayのページに戻る必要があるかもしれません。

  1. reverse関数を雛形にしたtranspose関数を実装してください。この関数はMatrixを引数として受け取り、要素のうち2つを入れ替えたものを返します。つまり

    println!("Matrix:\n{}", matrix);
    println!("Transpose:\n{}", transpose(matrix));
    

    は以下の様な出力になります:

    Matrix:
    ( 1.1 1.2 )
    ( 2.1 2.2 )
    Transpose:
    ( 1.1 2.1 )
    ( 1.2 2.2 )
    

配列とスライス

配列はTという単一の型(訳注: ジェネリック型でも可)のオブジェクトの集合です。それらのオブジェクトはメモリ上の連続した領域に保存されます。配列は[]を用いて生成されます。長さはコンパイル時には決定されていて、[T; length]という形で指定できます。

スライスは配列に似ていますが、コンパイル時に長さが決定されていません。スライスは2ワードからなるオブジェクトであり、最初のワードがデータへのポインタ、2番目のワードがスライスの長さです。ワード長はusizeと同一で、プロセッサのアーキテクチャによって決まります。例えばx86-64では64ビットです。スライスは配列の一部を借用するのに使用され、&[T]という型シグネチャを持ちます。

use std::mem;

// This function borrows a slice
// この関数はスライスを借用する
fn analyze_slice(slice: &[i32]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Fixed-size array (type signature is superfluous)
    // 固定長の配列(型シグネチャは冗長なので、なくても可)
    let xs: [i32; 5] = [1, 2, 3, 4, 5];

    // All elements can be initialized to the same value
    // すべての要素を0にする場合
    let ys: [i32; 500] = [0; 500];

    // Indexing starts at 0
    // インデックスは0から
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // `len` returns the count of elements in the array
    // `len`は配列の要素数を返す。
    println!("number of elements in array: {}", xs.len());

    // Arrays are stack allocated
    // 配列はスタック上に置かれる
    println!("array occupies {} bytes", mem::size_of_val(&xs));

    // Arrays can be automatically borrowed as slices
    // 配列は自動的にスライスとして借用される。
    println!("borrow the whole array as a slice");
    analyze_slice(&xs);

    // Slices can point to a section of an array
    // They are of the form [starting_index..ending_index]
    // starting_index is the first position in the slice
    // ending_index is one more than the last position in the slice
    // スライスは配列の一部を指すことができる。
    // [starting_index..ending_index] の形をとり、
    // starting_index はスライスの先頭の位置を表し、
    // ending_index はスライスの末尾の1つ先の位置を表す。
    println!("borrow a section of the array as a slice");
    analyze_slice(&ys[1 .. 4]);

    // Out of bound indexing causes compile error
    // インデックスが範囲外のときはコンパイルエラー
    println!("{}", xs[5]);
}

カスタム型

Rustでのカスタムデータ型の作成は主に以下の2つのキーワードを介して行われます。

  • struct: 構造体を定義する
  • enum: 列挙型を定義する

const、あるいはstaticというキーワードによって定数を定義することもできます。

構造体

structというキーワードを用いて作成できる構造体には3種類あります。

  • タプル。(すなわちタプルに名前が付いたようなもの)
  • クラシックなC言語スタイルの構造体。
  • ユニット。これはフィールドを持たず、ジェネリック型を扱う際に有効です。
#[derive(Debug)]
struct Person {
    // The 'a defines a lifetime
    name: String,
    age: u8,
}

// A unit struct
// ユニット
struct Unit;

// A tuple struct
// タプル
struct Pair(i32, f32);

// A struct with two fields
// 2つのフィールドを持つ(クラシックな)構造体
struct Point {
    x: f32,
    y: f32,
}

// Structs can be reused as fields of another struct
// 構造体は他の構造体のフィールドになることができる
#[allow(dead_code)]
struct Rectangle {
    // A rectangle can be specified by where the top left and bottom right
    // corners are in space.
    // 長方形は座標空間上における左上隅と右下隅の位置によって指定できる
    top_left: Point,
    bottom_right: Point,
}

fn main() {
    // Create struct with field init shorthand
    // 構造体をフィールド初期化の簡略記法で生成
    let name = String::from("Peter");
    let age = 27;
    let peter = Person { name, age };

    // Print debug struct
    // 構造体のデバッグ表示を行う
    println!("{:?}", peter);


    // Instantiate a `Point`
    // `Point` のインスタンス化
    let point: Point = Point { x: 10.3, y: 0.4 };

    // Access the fields of the point
    // pointのフィールドにアクセスする。
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Make a new point by using struct update syntax to use the fields of our
    // other one
    // 構造体の更新記法を用いて、別の構造体のフィールドの値を基に
    // 新たなpointを生成
    let bottom_right = Point { x: 5.2, ..point };

    // `bottom_right.y` will be the same as `point.y` because we used that field
    // from `point`
    // `bottom_right.y` の値は `point.y` と同一になるが、
    // これは `point` のフィールドの値を用いて生成したためである
    println!("second point: ({}, {})", bottom_right.x, bottom_right.y);

    // Destructure the point using a `let` binding
    // `let`を使用してpointをデストラクトする。
    let Point { x: left_edge, y: top_edge } = point;

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        // 構造体の定義とインスタンスの作成を同時に行う
        top_left: Point { x: left_edge, y: top_edge },
        bottom_right: bottom_right,
    };

    // Instantiate a unit struct
    // ユニットをインスタンス化
    let _unit = Unit;

    // Instantiate a tuple struct
    // タプルをインスタンス化
    let pair = Pair(1, 0.1);

    // Access the fields of a tuple struct
    // タプルのフィールドにアクセス
    println!("pair contains {:?} and {:?}", pair.0, pair.1);

    // Destructure a tuple struct
    // タプルをデストラクト
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);
}

演習

  1. Rectangle の面積を計算する rect_area 関数を追加してください。ネストしたデストラクトを使ってみましょう。
  2. Pointf32 を引数とした時に Rectangle を返す square 関数を追加してください。 Rectangle の左下の点が Point になり、f32Rectangle の幅と高さになります。

See also

アトリビュート(attributes), デストラクト

列挙型

列挙型(enum)はいくつかの異なる要素型の中から1つを選ぶような場合に使用します。構造体(struct)の定義を満たすものならば何でもenum の要素型として使用できます。

// Create an `enum` to classify a web event. Note how both
// names and type information together specify the variant:
// `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`.
// Each is different and independent.
// `enum`を作成してwebイベントを分類する。
// 名前と型情報を併せたものが要素型になっていることに注意。
// `PageLoad != PageUnload` であり、
// `KeyPress(char) != Paste(String)` である。
// 要素型は互いに異なり、互いに非依存である。
enum WebEvent {
    // An `enum` may either be `unit-like`,
    // `enum`要素型はユニット風でもよい
    PageLoad,
    PageUnload,
    // like tuple structs,
    // タプル風でもよい
    KeyPress(char),
    Paste(String),
    // or c-like structures.
    // C言語スタイルの構造体風でもよい
    Click { x: i64, y: i64 },
}

// A function which takes a `WebEvent` enum as an argument and
// returns nothing.
// 引数として`WebEvent`列挙型をとり、何も返さない関数
fn inspect(event: WebEvent) {
    match event {
        WebEvent::PageLoad => println!("page loaded"),
        WebEvent::PageUnload => println!("page unloaded"),
        // Destructure `c` from inside the `enum`.
        WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
        WebEvent::Paste(s) => println!("pasted \"{}\".", s),
        // Destructure `Click` into `x` and `y`.
        WebEvent::Click { x, y } => {
            println!("clicked at x={}, y={}.", x, y);
        },
    }
}

fn main() {
    let pressed = WebEvent::KeyPress('x');
    // `to_owned()` creates an owned `String` from a string slice.
    // `to_owned()`は文字列スライスから所有権のある`String`を作成する
    let pasted  = WebEvent::Paste("my text".to_owned());
    let click   = WebEvent::Click { x: 20, y: 80 };
    let load    = WebEvent::PageLoad;
    let unload  = WebEvent::PageUnload;

    inspect(pressed);
    inspect(pasted);
    inspect(click);
    inspect(load);
    inspect(unload);
}

型エイリアス

型エイリアスを用いると、列挙型の要素型を別名で参照できます。これは列挙型の名前があまりに長かったり、あまりに一般的だったりで改名したい場合に役立ちます。

enum VeryVerboseEnumOfThingsToDoWithNumbers {
    Add,
    Subtract,
}

// Creates a type alias
// 型エイリアスを作成する
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;

fn main() {
    // We can refer to each variant via its alias, not its long and inconvenient
    // name.
    // 長くて不便な列挙型の名前ではなく、別名を使って要素型を参照できる
    let x = Operations::Add;
}

このやり方がもっともよく見られるのは、implブロックでSelfという別名を使用する場合です。

enum VeryVerboseEnumOfThingsToDoWithNumbers {
    Add,
    Subtract,
}

impl VeryVerboseEnumOfThingsToDoWithNumbers {
    fn run(&self, x: i32, y: i32) -> i32 {
        match self {
            Self::Add => x + y,
            Self::Subtract => x - y,
        }
    }
}

列挙型や型エイリアスについて詳しく学びたい人は、この機能が安定してRustに取り込まれた後にstabilization reportを読んでください。

参照

マッチ(match), 関数(fn), 文字列(String), "Type alias enum variants" RFC

use

useを使用すれば変数のスコープを絶対名で指定する必要がなくなる。

// An attribute to hide warnings for unused code.
// 使用されていないコードよる警告を隠すアトリビュート
#![allow(dead_code)]

enum Status {
    Rich,
    Poor,
}

enum Work {
    Civilian,
    Soldier,
}

fn main() {
    // Explicitly `use` each name so they are available without
    // `use`することで絶対名でなくとも使用可能になる。
    // manual scoping.
    use crate::Status::{Poor, Rich};
    // Automatically `use` each name inside `Work`.
    // `Work`の中の名前をすべて`use`する
    use crate::Work::*;

    // Equivalent to `Status::Poor`.
    // `use`しているため、`Status::Poor`と書いていることに等しい
    let status = Poor;
    // Equivalent to `Work::Civilian`.
    // `Work::Civilian`に等しい
    let work = Civilian;

    match status {
        // Note the lack of scoping because of the explicit `use` above.
        // `use`しているのでスコープを明示していない
        Rich => println!("The rich have lots of money!"),
        Poor => println!("The poor have no money..."),
    }

    match work {
        // Note again the lack of scoping.
        // こちらも同じ
        Civilian => println!("Civilians work!"),
        Soldier  => println!("Soldiers fight!"),
    }
}

参照

マッチ(match), use

C言語ライクな列挙型

列挙型はC言語の列挙型のような使い方をする事もできます。

// An attribute to hide warnings for unused code.
// 使用されていないコードによる警告を抑えるアトリビュート
#![allow(dead_code)]

// enum with implicit discriminator (starts at 0)
// 値を明示しない場合、0から整数が順に入る。
enum Number {
    Zero,
    One,
    Two,
}

// enum with explicit discriminator
// 値を明示する場合
enum Color {
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {
    // `enums` can be cast as integers.
    // 列挙型の中身を整数としてキャストする。
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}

参照

キャスティング

テストケース: 連結リスト

enumを使用が適切なパターンのひとつに、連結リスト(linked-list)を作成する場合があります。

use crate::List::*;

enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    // Cons: これは、要素をラップし、次の要素へのポインタを保持するタプル。
    Cons(u32, Box<List>),
    // Nil: A node that signifies the end of the linked list
    // Nil: 連結リストの終端であることを示すノード
    Nil,
}

// Methods can be attached to an enum
// 列挙型にはメソッドを付与することができる。
impl List {
    // Create an empty list
    // 空リストの作成。
    fn new() -> List {
        // `Nil` has type `List`
        // `Nil` は `List`型を持つ。
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    // リストを受け取り、その始端に新しい要素を付加したものを返す関数。
    fn prepend(self, elem: u32) -> List {
        // `Cons` also has type List
        // この`Cons` 自体も、その第2要素もどちらもlist型である。
        Cons(elem, Box::new(self))
    }

    // Return the length of the list
    // list の長さを返すメソッド
    fn len(&self) -> u32 {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&List`, and `*self` has type `List`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        // after Rust 2018 you can use self here and tail (with no ref) below as well,
        // rust will infer &s and ref tail. 
        // See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
        // このメソッドは、`self`の状態によって振る舞いが
        // 変化するため、matchをする必要がある。
        // `self`の型は`&List`であるので、`*self`は`List`になる。マッチングは
        // リファレンス(`&T`)ではなく実体(`T`)に対して行うのが好ましい。
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            // `self`をすでに借用しているので、tailの所有権を取ることができない。
            // 代わりに参照を使用する。
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            // 空リストならば長さは0
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string
    // Listをheap上の文字列として表したものを返すメソッド。
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                // `format!`は`print!`に似ているが、コンソール上に出力
                // する代わりに、heap上の文字列を返す。
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    // 空の連結リストを作成
    let mut list = List::new();

    // Prepend some elements
    // 要素を追加
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    // 追加後の状態を表示
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

参照

ボックス(Box), メソッド

定数

Rustには2種類の定数があり、いずれもグローバルスコープを含む任意のスコープで宣言することができます。また、いずれも型を明示しなくてはなりません。

  • const: 不変の値(通常はこちらを使用する)
  • static: スタティックなライフタイムを持つミュータブル(mut)な値 The static lifetime is inferred and does not have to be specified. Accessing or modifying a mutable static variable is unsafe.
// Globals are declared outside all other scopes.
// グローバル変数はあらゆるスコープの外で宣言します
static LANGUAGE: &str = "Rust";
const THRESHOLD: i32 = 10;

fn is_big(n: i32) -> bool {
    // Access constant in some function
    // 関数内から定数を参照
    n > THRESHOLD
}

fn main() {
    let n = 16;

    // Access constant in the main thread
    // main 関数の中から定数を参照
    println!("This is {}", LANGUAGE);
    println!("The threshold is {}", THRESHOLD);
    println!("{} is {}", n, if is_big(n) { "big" } else { "small" });

    // Error! Cannot modify a `const`.
    // エラー!`const`は変更できません。
    THRESHOLD = 5;
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう
}

参照

const 及び static の RFC, 'static ライフタイム

変数束縛

Rustは静的(static)な型付けゆえに型安全です。変数束縛は宣言時に型を指定できます。とはいえたいていの場合は、コンパイラは変数の型をコンテキストから推測することができますので、型指定の負担を大幅に軽減できます。

値(リテラルなど)はletを用いて変数に束縛することができます。

fn main() {
    let an_integer = 1u32;
    let a_boolean = true;
    let unit = ();

    // copy `an_integer` into `copied_integer`
    // `an_integer`を`copied_integer`へとコピー
    let copied_integer = an_integer;

    println!("An integer: {:?}", copied_integer);
    println!("A boolean: {:?}", a_boolean);
    println!("Meet the unit value: {:?}", unit);

    // The compiler warns about unused variable bindings; these warnings can
    // be silenced by prefixing the variable name with an underscore
    // 使用されていない変数があると、コンパイラは警告を出します。
    // 変数名の頭に`_`(アンダーバー)を付けると警告を消すことができます。
    let _unused_variable = 3u32;

    let noisy_unused_variable = 2u32;
    // FIXME ^ Prefix with an underscore to suppress the warning
    // FIXME ^ 頭にアンダーバーを付けて、警告を抑えましょう。
}

ミュータビリティ

変数はデフォルトでイミュータブル(変更不可能)ですがmut構文を使用することでミュータブルになります。

fn main() {
    let _immutable_binding = 1;
    let mut mutable_binding = 1;

    println!("Before mutation: {}", mutable_binding);

    // Ok
    mutable_binding += 1;

    println!("After mutation: {}", mutable_binding);

    // Error!
    _immutable_binding += 1;
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう
}

コンパイラはミュータビリティに関するエラーの詳細を出してくれます。

スコープとシャドーイング

変数はスコープを持つため、 ブロック の中に閉じ込められています。ブロックとは{}で囲まれた領域のことです。

fn main() {
    // This binding lives in the main function
    // この変数はmain関数内が生息域です。
    let long_lived_binding = 1;

    // This is a block, and has a smaller scope than the main function
    // ここから下が`main`より小さいスコープを持つブロックとなります。
    {
        // This binding only exists in this block
        // この変数はこのブロック内のみに存在します。
        let short_lived_binding = 2;

        println!("inner short: {}", short_lived_binding);

        // This binding *shadows* the outer one
        // この変数はスコープ外の同名の変数を *シャドーイング* します。
        let long_lived_binding = 5_f32;

        println!("inner long: {}", long_lived_binding);
    }
    // End of the block
    // ブロックの終わり

    // Error! `short_lived_binding` doesn't exist in this scope
    // `short_lived_binding`はこのスコープ内には存在しませんのでエラーとなります。
    println!("outer short: {}", short_lived_binding);
    // FIXME ^ Comment out this line
    // FIXME ^ コメントアウトしましょう

    println!("outer long: {}", long_lived_binding);
    
    // This binding also *shadows* the previous binding
    // この変数バインディングも以前に定義した変数を *シャドーイング* します
    let long_lived_binding = 'a';
    
    println!("outer long: {}", long_lived_binding);
}

Also, variable shadowing is allowed.

fn main() {
    let shadowed_binding = 1;

    {
        println!("before being shadowed: {}", shadowed_binding);

        // This binding *shadows* the outer one
        let shadowed_binding = "abc";

        println!("shadowed in inner block: {}", shadowed_binding);
    }
    println!("outside inner block: {}", shadowed_binding);

    // This binding *shadows* the previous binding
    let shadowed_binding = 2;
    println!("shadowed in outer block: {}", shadowed_binding);
}

宣言

変数の宣言だけを行っておき、初期化(定義)をのちに行うことも可能です。 しかし、最後まで初期化されない変数が生じる可能性があるため、ふつうは同時に行われます。

fn main() {
    // Declare a variable binding
    // 変数を宣言
    let a_binding;

    {
        let x = 2;

        // Initialize the binding
        // 変数を初期化
        a_binding = x * x;
    }

    println!("a binding: {}", a_binding);

    let another_binding;

    // Error! Use of uninitialized binding
    // エラー! 初期化していない変数の使用
    println!("another binding: {}", another_binding);
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう。

    another_binding = 1;

    println!("another binding: {}", another_binding);
}

未初期化の変数があると予期せぬ動作をする場合があるため、コンパイラは変数を初期化してから使用するよう強制します。

値の凍結

データを同じ名前のイミュータブルな変数に束縛しなおすと、データは凍結されます。凍結したデータは、イミュータブルな束縛がスコープ外になるまで変更できません。

fn main() {
    let mut _mutable_integer = 7i32;

    {
        // Shadowing by immutable `_mutable_integer`
        // イミュータブルな`_mutable_integer`でシャドーイングする
        let _mutable_integer = _mutable_integer;

        // Error! `_mutable_integer` is frozen in this scope
        // エラー! `_mutable_integer`はこのスコープでは凍結している。
        _mutable_integer = 50;
        // FIXME ^ Comment out this line
        // FIXME ^ この行をコメントアウトしましょう。

        // `_mutable_integer` goes out of scope
        // `_mutable_integer`はスコープを抜ける
    }

    // Ok! `_mutable_integer` is not frozen in this scope
    // OK! `_mutable_integer`はこのスコープでは凍結していない。
    _mutable_integer = 3;
}

Rustには、プリミティブ型やユーザ定義型を定義したり変換したりする様々な方法があります。 この章は以下の内容を扱います:

型キャスティング

Rustはプリミティブ型における強制的な型変換を暗黙に行うことはありません。しかし明示的に行うこと(casting)は可能です。その場合asキーワードを使用します。

整数型から整数型へ型変換する場合、C言語で可能なケースの場合はC言語と同じです。 C言語で未定義の場合の挙動も、Rustでは完全に定義されています。

// Suppress all warnings from casts which overflow.
// オーバーフローを起こすようなキャスティングによる警告を無視する。
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // Error! No implicit conversion
    // エラー! 暗黙的な型変換はできない。
    let integer: u8 = decimal;
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう。

    // Explicit conversion
    // 明示的な型変換
    let integer = decimal as u8;
    let character = integer as char;

    // Error! There are limitations in conversion rules. 
    // A float cannot be directly converted to a char.
    let character = decimal as char;
    // FIXME ^ Comment out this line

    println!("Casting: {} -> {} -> {}", decimal, integer, character);

    // when casting any value to an unsigned type, T,
    // T::MAX + 1 is added or subtracted until the value
    // fits into the new type
    // 何らかの値を符号なしの型(仮にTとする)へキャスティングすると
    // 値がTに収まるまで、T::MAX + 1 が加算あるいは減算される。

    // 1000 already fits in a u16
    // 1000 はすでにu16に収まっているため変化しない。
    println!("1000 as a u16 is: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    // Under the hood, the first 8 least significant bits (LSB) are kept,
    // while the rest towards the most significant bit (MSB) get truncated.
    // 水面下では最下位ビットから8bitが使用され、残りの上位ビットが圧縮される形になる。
    println!("1000 as a u8 is : {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 as a u8 is : {}", (-1i8) as u8);

    // For positive numbers, this is the same as the modulus
    println!("1000 mod 256 is : {}", 1000 % 256);

    // When casting to a signed type, the (bitwise) result is the same as
    // first casting to the corresponding unsigned type. If the most significant
    // bit of that value is 1, then the value is negative.
    // 符号付きの型にキャストする場合、結果は以下の2つを行った場合に等しい
    // 1. 対応する符号なしの型にキャストする。
    // 2. 2の補数(two's complement)をとる

    // Unless it already fits, of course.
    println!(" 128 as a i16 is: {}", 128 as i16);
    // 128 as u8 -> 128, whose two's complement in eight bits is:
    // 128をu8にキャストすると128となる。128の8ビットにおける補数は -128
    println!(" 128 as a i8 is : {}", 128 as i8);

    // repeating the example above
    // 上で示した例から
    // 1000 as u8 -> 232
    println!("1000 as a u8 is : {}", 1000 as u8);
    // and the two's complement of 232 is -24
    // が成り立つ。232の8ビットにおける補数は -24
    println!(" 232 as a i8 is : {}", 232 as i8);
    
    // Since Rust 1.45, the `as` keyword performs a *saturating cast* 
    // when casting from float to int. If the floating point value exceeds 
    // the upper bound or is less than the lower bound, the returned value 
    // will be equal to the bound crossed.
    
    // 300.0 is 255
    println!("300.0 is {}", 300.0_f32 as u8);
    // -100.0 as u8 is 0
    println!("-100.0 as u8 is {}", -100.0_f32 as u8);
    // nan as u8 is 0
    println!("nan as u8 is {}", f32::NAN as u8);
    
    // This behavior incurs a small runtime cost and can be avoided 
    // with unsafe methods, however the results might overflow and 
    // return **unsound values**. Use these methods wisely:
    unsafe {
        // 300.0 is 44
        println!("300.0 is {}", 300.0_f32.to_int_unchecked::<u8>());
        // -100.0 as u8 is 156
        println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::<u8>());
        // nan as u8 is 0
        println!("nan as u8 is {}", f32::NAN.to_int_unchecked::<u8>());
    }
}

リテラル

数値型リテラルはサフィックスにより型を指定することが可能です。例えば、42というリテラルに対してi32型を指定するには42i32とします。

サフィックスを指定しない数値型リテラルの場合、その型はどのように使用されるかに依存して決められます。デフォルトでは整数型の場合i32が、浮動小数点型はf64を使用します。

fn main() {
    // Suffixed literals, their types are known at initialization
    // サフィックスを指定したリテラル。型は初期化とともに確定する。
    let x = 1u8;
    let y = 2u32;
    let z = 3f32;

    // Unsuffixed literals, their types depend on how they are used
    // サフィックスを指定しないリテラル。型は使用方法に依存する。
    let i = 1;
    let f = 1.0;

    // `size_of_val` returns the size of a variable in bytes
    // `size_of_val` 関数は変数のサイズをバイトで返す。
    println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));
    println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));
    println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));
    println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
    println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));
}

上のコードには現時点では解説していない考えがいくつか使用されています。気になる方のために簡単に説明をしておきましょう。

  • std::mem::size_of_valは関数ですが、 絶対パス(full path) で呼び出されています。ソースコードは論理的に区切られた モジュール と呼ばれるものにわけられることができます。今回の場合はsize_of_val関数はmemモジュール内で定義されており、memモジュールはstd クレート 内で定義されています。より詳しくはクレート(crates)を参照してください。

型推論

Rustの型推論エンジンはなかなか賢くできています。初期化の際に評価値の型をチェックするだけでなく、その後にどのような使われ方をしているかを見て推論します。以下がその例です。

fn main() {
    // Because of the annotation, the compiler knows that `elem` has type u8.
    // アノテーションのおかげで、コンパイラは`elem`がu8型であることがわかる。
    let elem = 5u8;

    // Create an empty vector (a growable array).
    // 空のベクトル(可変長の配列)を生成
    let mut vec = Vec::new();
    // At this point the compiler doesn't know the exact type of `vec`, it
    // just knows that it's a vector of something (`Vec<_>`).
    // この時点でコンパイラは`vec`の型を知らず、
    // 単に何らかの値のベクトル(`Vec<_>`)であるということだけを把握している。

    // Insert `elem` in the vector.
    // `elem`をベクトルに挿入
    vec.push(elem);
    // Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec<u8>`)
    // TODO ^ Try commenting out the `vec.push(elem)` line
    // よし! これでコンパイラは`vec`が`u8`のベクトル(`Vec<u8>`)であることを把握する。
    // TODO ^ 上の `vec.push(elem)` をコメントアウトしてみましょう。

    println!("{:?}", vec);
}

このように、変数の型アノテーションは必要ありません。これでコンパイラもプログラマもハッピーですね!

エイリアス

type文を使用することで既存の型に新しい名前(alias)を付けることができます。その場合、名前はUpperCamelCaseでなくてはなりません。さもなくばコンパイラがエラーを出します。唯一の例外はusizef32のようなプリミティブ型です。

// `NanoSecond` is a new name for `u64`.
// `NanoSecond` を `u64`の別名として使用する。
type NanoSecond = u64;
type Inch = u64;

// Use an attribute to silence warning.
// 警告を抑えるアトリビュートを使用。
#[allow(non_camel_case_types)]
type u64_t = u64;
// TODO ^ Try removing the attribute
// TODO ^ アトリビュートを使用しない場合、どうなるか見てみましょう。

fn main() {
    // `NanoSecond` = `Inch` = `u64_t` = `u64`.
    let nanoseconds: NanoSecond = 5 as u64_t;
    let inches: Inch = 2 as u64_t;

    // Note that type aliases *don't* provide any extra type safety, because
    // aliases are *not* new types
    // 型のエイリアスは、元の型をより型安全にしてくれる **わけではない** ことに注意しましょう。
    // なぜならば、エイリアスは新たな型を定義している **わけではない** からです。
    println!("{} nanoseconds + {} inches = {} unit?",
             nanoseconds,
             inches,
             nanoseconds + inches);
}

このようにエイリアスを付ける一番の理由はボイラープレートを減らすことです。例えばIoResult<T>型はResult<T, IoError>の別名です。

参照

アトリビュート

型変換

Primitive types can be converted to each other through casting.

Rustはカスタム型(例えばstructenum)間の変換をトレイトを用いて行います。ジェネリックな型変換にはFromおよびIntoトレイトを使用します。しかし、よくあるケースにおいて、特にStringとの相互の型変換では、特殊なトレイトが使用されます。

FromおよびInto

FromトレイトとIntoトレイトは本質的に結びついており、そのことが実際に実装に反映されています。もし型Aから型Bへの変換ができるのであれば、型Bから型Aへの変換もできると思うのが自然です。

From

Fromトレイトは、ある型に対し、別の型からその型を作る方法を定義できるようにするものです。そのため、複数の型の間で型変換を行うための非常にシンプルな仕組みを提供しています。標準ライブラリでは、基本データ型やよく使われる型に対して、このトレイトが多数実装されています。

例えば、strからStringへの型変換は簡単です。


#![allow(unused)]
fn main() {
let my_str = "hello";
let my_string = String::from(my_str);
}

自作の型に対しても、型変換を定義すれば同じように行えます。

use std::convert::From;

#[derive(Debug)]
struct Number {
    value: i32,
}

impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

fn main() {
    let num = Number::from(30);
    println!("My number is {:?}", num);
}

Into

Intoトレイトは、単にFromトレイトの逆の働きをします。もし自作の型にFromトレイトが実装されていたら、Intoは必要に応じてそれを呼び出します。

Intoトレイトを使用すると、ほとんどの場合、コンパイラが型を決定することができないため、変換する型を指定する必要があります。しかし、この機能を無料で得られることを考えれば、これは小さなトレードオフです。

use std::convert::From;

#[derive(Debug)]
struct Number {
    value: i32,
}

impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

fn main() {
    let int = 5;
    // Try removing the type declaration
    let num: Number = int.into();
    println!("My number is {:?}", num);
}

TryFromおよびTryInto

FromおよびIntoと同様に、TryFromおよびTryIntoも型変換を行うジェネリックなトレイトです。From/Intoと異なり、TryFrom/TryIntoトレイトは失敗する可能性のある型変換に用いられるので、Resultを返します。

use std::convert::TryFrom;
use std::convert::TryInto;

#[derive(Debug, PartialEq)]
struct EvenNumber(i32);

impl TryFrom<i32> for EvenNumber {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value % 2 == 0 {
            Ok(EvenNumber(value))
        } else {
            Err(())
        }
    }
}

fn main() {
    // TryFrom

    assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
    assert_eq!(EvenNumber::try_from(5), Err(()));

    // TryInto

    let result: Result<EvenNumber, ()> = 8i32.try_into();
    assert_eq!(result, Ok(EvenNumber(8)));
    let result: Result<EvenNumber, ()> = 5i32.try_into();
    assert_eq!(result, Err(()));
}

Stringとの型変換

Stringへの型変換

任意の型をStringに変換するのは簡単で、その型にToStringトレイトを実装するだけです。これを直接実装するよりも、fmt::Displayトレイトを実装するのがよいでしょう。そうすることで自動的にToStringが提供されるだけでなく、print!の章で説明したように、その型を表示できるようにもなります。

use std::fmt;

struct Circle {
    radius: i32
}

impl fmt::Display for Circle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Circle of radius {}", self.radius)
    }
}

fn main() {
    let circle = Circle { radius: 6 };
    println!("{}", circle.to_string());
}

Stringの解析

文字列からの型変換において、数値への型変換はよく行われるものの一つです。これを行うイディオムはparse関数を使用することですが、このときに型を推論できるようにするか、もしくは turbofish構文を使用して型を指定するかのいずれかを行います。以下の例では、どちらの方法も紹介しています。

parse関数は、指定された型にFromStrトレイトが実装されていれば、文字列をその型に変換します。このトレイトは標準ライブラリの多くの型に対して実装されています。ユーザー定義の型でこの機能を利用するには、その型に対してFromStrトレイトを実装するだけです。

fn main() {
    let parsed: i32 = "5".parse().unwrap();
    let turbo_parsed = "10".parse::<i32>().unwrap();

    let sum = parsed + turbo_parsed;
    println!("Sum: {:?}", sum);
}

Rustのプログラムは(ほとんどの場合)文(statement)の連続でできています

fn main() {
    // statement
    // statement
    // statement
}

宣言文にはいくつかの種類があります。最も一般的なのは変数の束縛(variable binding)と;付きの式(expression)です

fn main() {
    // variable binding
    // 変数束縛
    let x = 5;

    // expression;
    // 式;
    x;
    x + 1;
    15;
}

コードブロックも式の一種です。よってブロックを丸ごと値として扱うことができます。その場合ブロック内の最後の式が場所を表す式(例えばローカル変数)に代入されます。ただし、ブロック内の最後の式が;で終わる場合は返り値は()になります。

fn main() {
    let x = 5u32;

    let y = {
        let x_squared = x * x;
        let x_cube = x_squared * x;

        // This expression will be assigned to `y`
        // この式は`y`に代入されます。
        x_cube + x_squared + x
    };

    let z = {
        // The semicolon suppresses this expression and `()` is assigned to `z`
        // セミコロンがあるので`z`には`()`が入ります。
        2 * x;
    };

    println!("x is {:?}", x);
    println!("y is {:?}", y);
    println!("z is {:?}", z);
}

条件分岐

処理の流れをコントロールすることはあらゆるプログラミング言語において重要な要素です。

if/else, for等です。Rustの文法を見ていきましょう。

if/else

if-elseを用いた条件分岐は他の言語に似ています。多くの言語では条件式の中を括弧でくくる必要がありますが、Rustではその必要はありません。条件式の直後にはブロックが続きます。if-elseは式の一種で、いずれの分岐先でも返り値の型は同一でなくてはなりません。

fn main() {
    let n = 5;

    if n < 0 {
        print!("{} is negative", n);
    } else if n > 0 {
        print!("{} is positive", n);
    } else {
        print!("{} is zero", n);
    }

    let big_n =
        if n < 10 && n > -10 {
            println!(", and is a small number, increase ten-fold");

            // This expression returns an `i32`.
            // この式は`i32`を返す。
            10 * n
        } else {
            println!(", and is a big number, halve the number");

            // This expression must return an `i32` as well.
            // ここでも返り値の型は`i32`でなくてはならない。
            n / 2
            // TODO ^ Try suppressing this expression with a semicolon.
            // TODO ^ セミコロン(`;`)をつけて、返り値を返さないようにしてみましょう
        };
    //   ^ Don't forget to put a semicolon here! All `let` bindings need it.
    //   ここにセミコロンを付けるのを忘れないように!
    //   `let`による変数束縛の際には必ず必要です!

    println!("{} -> {}", n, big_n);
}

loop

Rustにはloopというキーワードが存在します。これは無限ループを作成するのに使用します。

訳注: while Trueと同じですが、ループのたびに条件を確認しないため、若干高速になります。

ループから抜けだす時はbreak, 即座に次のループに移るときはcontinueが使用できます。

fn main() {
    let mut count = 0u32;

    println!("Let's count until infinity!");

    // Infinite loop
    // 無限ループ
    loop {
        count += 1;

        if count == 3 {
            println!("three");

            // Skip the rest of this iteration
            // 残りの処理をスキップ
            continue;
        }

        println!("{}", count);

        if count == 5 {
            println!("OK, that's enough");

            // Exit this loop
            // ループを抜ける。
            break;
        }
    }
}

ネストとラベル

ネストしたループを回している時に外側のループをbreakまたはcontinueしたい場合があります。こういった場合にはlabelを用いてループにラベルを貼り、break/continueにそのラベルを渡します。

#![allow(unreachable_code)]

fn main() {
    'outer: loop {
        println!("Entered the outer loop");

        'inner: loop {
            println!("Entered the inner loop");

            // This would break only the inner loop
            // これは内側のループのみを中断します。
            //break;

            // This breaks the outer loop
            // こちらは外側を中断します
            break 'outer;
        }

        println!("This point will never be reached");
    }

    println!("Exited the outer loop");
}

loopが返す値

loopの用途のひとつに「成功するまである処理を再試行する」ことがあります。もしその処理が値を返すならば、それをコードの他の部分に渡す必要があるでしょう。breakの後に値を置くと、それがloop式の値として返されます。

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    assert_eq!(result, 20);
}

while

whileキーワードは条件が真である限り実行され続けるループのために使用します。

悪名高いFizzBuzz問題whileを用いて解いてみましょう。

fn main() {
    // A counter variable
    // カウンタとなる変数
    let mut n = 1;

    // Loop while `n` is less than 101
    // `n`が101以下である場合のループ
    while n < 101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }

        // Increment counter
        // カウンタに1を追加
        n += 1;
    }
}

forループ

for と range

for in文を用いることで、イテレータ(Iterator)のそれぞれの要素に対して処理をすることが可能です。イテレータを作る最も単純な方法はa..bのような書き方をすることです。これは「a」から「bのひとつ前」までの要素を順に産出(yield)するというものです。

ではforwhileを用いてFizzBuzzを書いてみましょう。

fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    // `n`は1, 2, ...., 100のそれぞれの値を取ります。
    for n in 1..101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

上記の代わりにa..=bを用いると、両端の値を含む範囲を指定できます。上記の例は次のように書けます。

fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    // `n`は1, 2, ...., 100のそれぞれの値を取ります。
    for n in 1..=100 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

forとイテレータ

for in構文はIteratorとさまざまな方法でやり取りできます。Iteratorトレイトの章で説明したように、デフォルトではforループにおいてinto_iter関数がコレクションに対して適用されます。しかし、コレクションをイテレータに変換する方法はこれだけではありません。

into_iteriteriter_mutはいずれもコレクションのイテレータへの変換を行いますが、データの「見せ方」の違いにより、そのやり方はそれぞれ異なります。

  • iter - この関数は、各周回においてコレクションの要素を借用します。よってコレクションには手を加えないので、ループの実行後もコレクションを再利用できます。
fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter() {
        match name {
            &"Ferris" => println!("There is a rustacean among us!"),
            // TODO ^ Try deleting the & and matching just "Ferris"
            _ => println!("Hello {}", name),
        }
    }
    
    println!("names: {:?}", names);
}
  • into_iter - この関数はコレクションからデータを取り出すので、各周回において要素のデータそのものが提供されます。データを取り出してしまうと、データはループ内に「移動」してしまうので、ループ実行後にコレクションを再利用することはできません。
fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.into_iter() {
        match name {
            "Ferris" => println!("There is a rustacean among us!"),
            _ => println!("Hello {}", name),
        }
    }
    
    println!("names: {:?}", names);
    // FIXME ^ Comment out this line
}
  • iter_mut - この関数はコレクションの各要素をミュータブル(変更可能)で借用するので、コレクションの要素をその場で変更できます。
fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut() {
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        }
    }

    println!("names: {:?}", names);
}

上記に示した3つのコードにおいて、matchの選択肢の型の違いに注意してください。ここがそれぞれの方法の違いを生む鍵になっています。型が異なれば、当然ながらそれに対して行える処理も変わります。

参照

イテレータ

match

Rustはmatchを用いて、C言語におけるswitchのようなパターンマッチングを行うことができます。 マッチする最初のアームが評価され、取りうるすべての値はカバーされていなければなりません。

fn main() {
    let number = 13;
    // TODO ^ Try different values for `number`

    println!("Tell me about {}", number);
    match number {
        // Match a single value
        // 単一の値とのマッチをチェック
        1 => println!("One!"),
        // Match several values
        // いくつかの値とのマッチをチェック
        2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
        // TODO ^ Try adding 13 to the list of prime values
        // TODO ^ 素数のリストに 13 を加えてみましょう
        // Match an inclusive range
        // 特定の範囲の値とのマッチをチェック
        13..=19 => println!("A teen"),
        // Handle the rest of cases
        // その他の場合の処理
        _ => println!("Ain't special"),
        // TODO ^ Try commenting out this catch-all arm
        // TODO ^ この全てをキャッチするアームをコメントアウトしてみましょう
    }

    let boolean = true;
    // Match is an expression too
    // マッチは式でもある。
    let binary = match boolean {
        // The arms of a match must cover all the possible values
        // マッチは全ての可能な値をカバーしなくてはならない
        false => 0,
        true => 1,
        // TODO ^ Try commenting out one of these arms
        // TODO ^ 試しに片方をコメントアウトしてみましょう。
    };

    println!("{} -> {}", boolean, binary);
}

デストラクト

matchは値をさまざまなやり方でデストラクトすることができます。

タプル

以下のように、タプルはmatchを用いてデストラクトすることができます。

fn main() {
    let triple = (0, -2, 3);
    // TODO ^ Try different values for `triple`
    // TODO ^ `triple`に別の値を入れてみましょう。

    println!("Tell me about {:?}", triple);
    // Match can be used to destructure a tuple
    // `match`を用いてタプルをデストラクトしてみましょう。
    match triple {
        // Destructure the second and third elements
        // 2つ目と3つ目の値をデストラクト
        (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
        (1, ..)  => println!("First is `1` and the rest doesn't matter"),
        // `..` can be used to ignore the rest of the tuple
        _      => println!("It doesn't matter what they are"),
        // `_` means don't bind the value to a variable
        // ここでは`_`は、値を変数に束縛しないことを意味します。
    }
}

参照

タプル

列挙型

列挙型(enum)も似たやり方でデストラクトすることができます。

// `allow` required to silence warnings because only
// one variant is used.
// `allow`は値を一つだけ使用したことによる警告を抑えるために存在する。
#[allow(dead_code)]
enum Color {
    // These 3 are specified solely by their name.
    // これら3つの値は名前のみで扱うことができる
    Red,
    Blue,
    Green,
    // These likewise tie `u32` tuples to different names: color models.
    // 以下の値は名前と`u32`のタプルをペアにしている。
    // カラーモデルと呼ばれる。
    RGB(u32, u32, u32),
    HSV(u32, u32, u32),
    HSL(u32, u32, u32),
    CMY(u32, u32, u32),
    CMYK(u32, u32, u32, u32),
}

fn main() {
    let color = Color::RGB(122, 17, 40);
    // TODO ^ Try different variants for `color`
    // TODO ^ `Color`に別の変数を入れてみましょう

    println!("What color is it?");
    // An `enum` can be destructured using a `match`.
    // `enum`は`match`を利用してデストラクトすることができる。
    match color {
        Color::Red   => println!("The color is Red!"),
        Color::Blue  => println!("The color is Blue!"),
        Color::Green => println!("The color is Green!"),
        Color::RGB(r, g, b) =>
            println!("Red: {}, green: {}, and blue: {}!", r, g, b),
        Color::HSV(h, s, v) =>
            println!("Hue: {}, saturation: {}, value: {}!", h, s, v),
        Color::HSL(h, s, l) =>
            println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l),
        Color::CMY(c, m, y) =>
            println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y),
        Color::CMYK(c, m, y, k) =>
            println!("Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
                c, m, y, k),
        // Don't need another arm because all variants have been examined
        // 全ての値を列挙したのでその他の場合の処理は必要ない。
    }
}

参照

#[allow(...)], カラーモデル, 列挙型

ポインタとref

Rustのポインタは、Cのポインタとは異なる概念なので、デストラクトとデリファレンスを同じようなやり方で扱うことはできない

  • デリファレンスには*を用いる。
  • デストラクトには&, ref, ref mutを用いる。
fn main() {
    // Assign a reference of type `i32`. The `&` signifies there
    // is a reference being assigned.
    // `i32`型へのリファレンスをアサインする。
    // `&`によってリファレンスであることを明示している。
    let reference = &4;

    match reference {
        // If `reference` is pattern matched against `&val`, it results
        // in a comparison like:
        // 上で定義した`reference`という変数が`&val`とのパターンマッチ
        // に用いられた場合、以下の2つの値が比較されていることになる。
        // `&i32`
        // `&val`
        // ^ We see that if the matching `&`s are dropped, then the `i32`
        // should be assigned to `val`.
        // ^ よって`&`を落とせば、`i32`が`val`にアサインされることがわかる。
        &val => println!("Got a value via destructuring: {:?}", val),
    }

    // To avoid the `&`, you dereference before matching.
    // `&`を使用したくない場合は、マッチングの前にデリファレンスする。
    match *reference {
        val => println!("Got a value via dereferencing: {:?}", val),
    }

    // What if you don't start with a reference? `reference` was a `&`
    // because the right side was already a reference. This is not
    // a reference because the right side is not one.
    // いきなりリファレンスを変数に代入するのではない場合はどうでしょう。
    // 先ほどは右辺値が`&`で始まっていたのでリファレンスでしたが、
    // これは違います。
    let _not_a_reference = 3;

    // Rust provides `ref` for exactly this purpose. It modifies the
    // assignment so that a reference is created for the element; this
    // reference is assigned.
    // このような場合、Rustでは変数束縛時に`ref`を宣言します。
    // 要素のリファレンスが作られて、それが束縛対象になります。
    let ref _is_a_reference = 3;

    // Accordingly, by defining 2 values without references, references
    // can be retrieved via `ref` and `ref mut`.
    // 同様にミュータブルな値の場合`ref mut`を使用することでリファレンスを
    // 取得できます。イミュータブルの場合と合わせてみていきましょう。
    let value = 5;
    let mut mut_value = 6;

    // Use `ref` keyword to create a reference.
    // `ref`を使用してリファレンスを作成。
    match value {
        ref r => println!("Got a reference to a value: {:?}", r),
    }

    // Use `ref mut` similarly.
    // 同様に`ref mut`を使用。
    match mut_value {
        ref mut m => {
            // Got a reference. Gotta dereference it before we can
            // add anything to it.
            // リファレンスを取得、値を変更するためにはデリファレンスする必要がある。
            *m += 10;
            println!("We added 10. `mut_value`: {:?}", m);
        },
    }
}

See also:

The ref pattern

構造体

以下のようにして、構造体(struct)も同様にデストラクトすることができる。

fn main() {
    struct Foo {
        x: (u32, u32),
        y: u32,
    }

    // Try changing the values in the struct to see what happens
    let foo = Foo { x: (1, 2), y: 3 };

    match foo {
        Foo { x: (1, b), y } => println!("First of x is 1, b = {},  y = {} ", b, y),

        // you can destructure structs and rename the variables,
        // the order is not important
        // 構造体をデストラクトして変数をリネーム
        // 順番は重要ではない。
        Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),

        // and you can also ignore some variables:
        // 一部の変数を無視することもできる。
        Foo { y, .. } => println!("y = {}, we don't care about x", y),
        // this will give an error: pattern does not mention field `x`
        // `x`に言及していないため、以下はエラーになる。
        //Foo { y } => println!("y = {}", y),
    }
}

参照

構造体

ガード

match内の条件文をフィルタリングするために、 ガード(guard) を使用することができます。

fn main() {
    let pair = (2, -2);
    // TODO ^ Try different values for `pair`
    // TODO ^ `pair`の値を変更してみましょう。

    println!("Tell me about {:?}", pair);
    match pair {
        (x, y) if x == y => println!("These are twins"),
        // The ^ `if condition` part is a guard
        //     ^ `if`とそれに続く条件式がガードです。
        (x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
        (x, _) if x % 2 == 1 => println!("The first one is odd"),
        _ => println!("No correlation..."),
    }
}

Note that the compiler does not check arbitrary expressions for whether all possible conditions have been checked. Therefore, you must use the _ pattern at the end.

fn main() {
    let number: u8 = 4;

    match number {
        i if i == 0 => println!("Zero"),
        i if i > 0 => println!("Greater than zero"),
        _ => println!("Fell through"), // This should not be possible to reach
    }
}

参照

タプル

バインディング

いくつかの変数をまとめてマッチ対象とした場合、そのうちの一つを分岐先で使用することはそのままでは不可能です。match内では@マークを使用して変数をバインディングすることができます。

// A function `age` which returns a `u32`.
// `age`関数は`u32`の値を返す。
fn age() -> u32 {
    15
}

fn main() {
    println!("Tell me what type of person you are");

    match age() {
        0             => println!("I haven't celebrated my first birthday yet"),
        // Could `match` 1 ..= 12 directly but then what age
        // would the child be? Instead, bind to `n` for the
        // sequence of 1 ..= 12. Now the age can be reported.
        // `1 ... 12`の値を一挙に`match`させることができる。
        // しかしその場合、子供は正確には何歳?
        // マッチした値を`n`にバインディングすることで値を使用できる。
        n @ 1  ..= 12 => println!("I'm a child of age {:?}", n),
        n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
        // Nothing bound. Return the result.
        // マッチしなかった場合の処理
        n             => println!("I'm an old person of age {:?}", n),
    }
}

You can also use binding to "destructure" enum variants, such as Option:

fn some_number() -> Option<u32> {
    Some(42)
}

fn main() {
    match some_number() {
        // Got `Some` variant, match if its value, bound to `n`,
        // is equal to 42.
        Some(n @ 42) => println!("The Answer: {}!", n),
        // Match any other number.
        Some(n)      => println!("Not interesting... {}", n),
        // Match anything else (`None` variant).
        _            => (),
    }
}

参照

関数, enums, Option

if let

列挙型をマッチさせるとき、場合によってはmatchを使用すると不自然な書き方になってしまう場合があります。例えば...


#![allow(unused)]
fn main() {
// Make `optional` of type `Option<i32>`
// `optional`という変数の型を`Option<i32>`に指定
let optional = Some(7);

match optional {
    Some(i) => {
        println!("This is a really long string and `{:?}`", i);
        // ^ Needed 2 indentations just so we could destructure
        // `i` from the option.
        // ^ `i`をoption型からデストラクトするためだけに
        // インデントが一つ増えてしまっている。
    },
    _ => {},
    // ^ Required because `match` is exhaustive. Doesn't it seem
    // like wasted space?
    // ^ `match`は全ての型に対して網羅的でなくてはならないので必要。
    // 冗長に見えませんか?
};

}

この場合はif letを用いたほうが美しく、失敗時の処理も柔軟に行うことができます。

fn main() {
    // All have type `Option<i32>`
    // 全て`Option<i32>`型
    let number = Some(7);
    let letter: Option<i32> = None;
    let emoticon: Option<i32> = None;

    // The `if let` construct reads: "if `let` destructures `number` into
    // `Some(i)`, evaluate the block (`{}`).
    // `if let`文は以下と同じ意味.
    //
    // もしletがnumberをデストラクトした結果が`Some(i)`になるならば
    // ブロック内(`{}`)を実行する。
    if let Some(i) = number {
        println!("Matched {:?}!", i);
    }

    // If you need to specify a failure, use an else:
    // デストラクトした結果が`Some()`にならない場合の処理を明示したい場合、
    // `else`を使用する。
    if let Some(i) = letter {
        println!("Matched {:?}!", i);
    } else {
        // Destructure failed. Change to the failure case.
        // デストラクト失敗の場合。このブロック内を実行
        println!("Didn't match a number. Let's go with a letter!");
    }

    // Provide an altered failing condition.
    // デストラクト失敗時の処理を更に分岐させることもできる
    let i_like_letters = false;

    if let Some(i) = emoticon {
        println!("Matched {:?}!", i);
    // Destructure failed. Evaluate an `else if` condition to see if the
    // alternate failure branch should be taken:
    // デストラクト失敗。`else if`を評価し、処理をさらに分岐させる。
    } else if i_like_letters {
        println!("Didn't match a number. Let's go with a letter!");
    } else {
        // The condition evaluated false. This branch is the default:
        // 今回は`else if`の評価がfalseなので、このブロック内がデフォルト
        println!("I don't like letters. Let's go with an emoticon :)!");
    }
}

In the same way, if let can be used to match any enum value:

// Our example enum
enum Foo {
    Bar,
    Baz,
    Qux(u32)
}

fn main() {
    // Create example variables
    let a = Foo::Bar;
    let b = Foo::Baz;
    let c = Foo::Qux(100);
    
    // Variable a matches Foo::Bar
    if let Foo::Bar = a {
        println!("a is foobar");
    }
    
    // Variable b does not match Foo::Bar
    // So this will print nothing
    if let Foo::Bar = b {
        println!("b is foobar");
    }
    
    // Variable c matches Foo::Qux which has a value
    // Similar to Some() in the previous example
    if let Foo::Qux(value) = c {
        println!("c is {}", value);
    }

    // Binding also works with `if let`
    if let Foo::Qux(value @ 100) = c {
        println!("c is one hundred");
    }
}

Another benefit is that if let allows us to match non-parameterized enum variants. This is true even in cases where the enum doesn't implement or derive PartialEq. In such cases if Foo::Bar == a would fail to compile, because instances of the enum cannot be equated, however if let will continue to work.

Would you like a challenge? Fix the following example to use if let:

// This enum purposely neither implements nor derives PartialEq.
// That is why comparing Foo::Bar == a fails below.
enum Foo {Bar}

fn main() {
    let a = Foo::Bar;

    // Variable a matches Foo::Bar
    if Foo::Bar == a {
    // ^-- this causes a compile-time error. Use `if let` instead.
        println!("a is foobar");
    }
}

参照

列挙型, オプション, RFC

while let

if letと同様に、while letも不格好なmatch処理を多少マシにしてくれます。例えば、以下のiをインクリメントする処理を見てください。


#![allow(unused)]
fn main() {
// Make `optional` of type `Option<i32>`
// `Option<i32>`の`optional`を作成
let mut optional = Some(0);

// Repeatedly try this test.
// 変数の照合を繰り返し行う。
loop {
    match optional {
        // If `optional` destructures, evaluate the block.
        // もし`optional`のデストラクトに成功した場合、値に応じて処理を分岐
        Some(i) => {
            if i > 9 {
                println!("Greater than 9, quit!");
                optional = None;
            } else {
                println!("`i` is `{:?}`. Try again.", i);
                optional = Some(i + 1);
            }
            // ^ Requires 3 indentations!
            // ^ 3つものインデントが必要。
        },
        // Quit the loop when the destructure fails:
        // デストラクトに失敗した場合、ループを脱出
        _ => { break; }
        // ^ Why should this be required? There must be a better way!
        // どうしてこんな行を書く必要が?もっと良い方法があるはずです!
    }
}
}

while letの使用によってベターになります。

fn main() {
    // Make `optional` of type `Option<i32>`
    // `Option<i32>`の`optional`を作成
    let mut optional = Some(0);

    // This reads: "while `let` destructures `optional` into
    // `Some(i)`, evaluate the block (`{}`). Else `break`.
    // これは次のように読める。「`let`が`optional`を`Some(i)`にデストラクトしている間は
    // ブロック内(`{}`)を評価せよ。さもなくば`break`せよ。」
    while let Some(i) = optional {
        if i > 9 {
            println!("Greater than 9, quit!");
            optional = None;
        } else {
            println!("`i` is `{:?}`. Try again.", i);
            optional = Some(i + 1);
        }
        // ^ Less rightward drift and doesn't require
        // explicitly handling the failing case.
        // ^ インデントが少なく、デストラクト失敗時の処理を追加で書く必要がない。
    }
    // ^ `if let` had additional optional `else`/`else if`
    // clauses. `while let` does not have these.
    // ^ `if let`の場合は`else`/`else if`句が一つ余分にあったが、
    // `while let`では必要が無い。
}

参照

列挙型(enum), Option, RFC

関数

関数はfnキーワードを用いて定義することができます。引数は変数と同様に型を指定する必要があり、もし関数が値を返すならば->の後にその型も指定する必要があります。

関数内の最後の式が返り値となります。関数の途中で値を返したい場合はreturn文を使用します。loopの最中やif文の中からも値を返すことができます。

では、もう一度FizzBuzz問題を解く関数を書いてみましょう!

// Unlike C/C++, there's no restriction on the order of function definitions
// C/C++とは違い、関数の定義を行う順番に制限はない。
fn main() {
    // We can use this function here, and define it somewhere later
    // ここで関数を使用し、後ほど定義してもかまわない。
    fizzbuzz_to(100);
}

// Function that returns a boolean value
// ブーリアン型を返す関数
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
    // Corner case, early return
    // 例外的な引数を受けた場合、早めに返す。
    if rhs == 0 {
        return false;
    }

    // This is an expression, the `return` keyword is not necessary here
    // これは式であり、`return`キーワードは必要ではない。
    lhs % rhs == 0
}

// Functions that "don't" return a value, actually return the unit type `()`
// 値を「返さない」関数、実際にはユニット型(`()`)を返している。
fn fizzbuzz(n: u32) -> () {
    if is_divisible_by(n, 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n, 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

// When a function returns `()`, the return type can be omitted from the
// signature
// 関数が`()`を返すとき、返り値の型を書く必要はない。
fn fizzbuzz_to(n: u32) {
    for n in 1..=n {
        fizzbuzz(n);
    }
}

関連関数とメソッド

関数には特定の型に紐づいたものがあります。これには関連関数とメソッドの2つの形式があります。 メソッドは特定のインスタンスに関連付けて呼ばれる関数であるのに対し、関連関数は型全体に対して定義される関数です。

struct Point {
    x: f64,
    y: f64,
}

// Implementation block, all `Point` associated functions & methods go in here
// 実装のためのブロック。`Point`の持つ関連関数とメソッドを全て定義する。
impl Point {
    // This is an "associated function" because this function is associated with
    // a particular type, that is, Point.
    // これは特定の型(すなわち Point)に関連した関数なので関連関数
    //
    // Associated functions don't need to be called with an instance.
    // These functions are generally used like constructors.
    // 関連関数はインスタンスからでなく呼び出すことができる。
    // 以下のようにコンストラクタとして使用されることが多い。
    fn origin() -> Point {
        Point { x: 0.0, y: 0.0 }
    }

    // Another associated function, taking two arguments:
    // もう一つ関連関数。引数を2つ取る。
    fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

impl Rectangle {
    // This is a method
    // `&self` is sugar for `self: &Self`, where `Self` is the type of the
    // caller object. In this case `Self` = `Rectangle`
    // こちらはメソッド。`&self`は`self: &Self`の糖衣構文。
    // `Self`は呼び出し元オブジェクトの型。この場合は`Rectangle`。
    fn area(&self) -> f64 {
        // `self` gives access to the struct fields via the dot operator
        // `self`はドット演算子によって構造体のfieldを参照できる。
        let Point { x: x1, y: y1 } = self.p1;
        let Point { x: x2, y: y2 } = self.p2;

        // `abs` is a `f64` method that returns the absolute value of the
        // caller
        // `abs`は`f64`のメソッドで、呼び出し元の値の絶対値を返す。
        ((x1 - x2) * (y1 - y2)).abs()
    }

    fn perimeter(&self) -> f64 {
        let Point { x: x1, y: y1 } = self.p1;
        let Point { x: x2, y: y2 } = self.p2;

        2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
    }

    // This method requires the caller object to be mutable
    // `&mut self` desugars to `self: &mut Self`
    // このメソッドは呼び出し元オブジェクトがミュータブルであることを
    // 必要とする。`&mut self`は`self: &mut Self`の糖衣構文である。
    fn translate(&mut self, x: f64, y: f64) {
        self.p1.x += x;
        self.p2.x += x;

        self.p1.y += y;
        self.p2.y += y;
    }
}

// `Pair` owns resources: two heap allocated integers
// `Pair`はヒープ上の整数を2つ保持する。
struct Pair(Box<i32>, Box<i32>);

impl Pair {
    // This method "consumes" the resources of the caller object
    // `self` desugars to `self: Self`
    // このメソッドは呼び出し元オブジェクトの持つ要素を「消費」する。
    // `self`は`self: Self`の糖衣構文である。
    fn destroy(self) {
        // Destructure `self`
        // `self`をデストラクト
        let Pair(first, second) = self;

        println!("Destroying Pair({}, {})", first, second);

        // `first` and `second` go out of scope and get freed
        // `first`、`second`はスコープから抜け出すと同時に、解放される。
    }
}

fn main() {
    let rectangle = Rectangle {
        // Associated functions are called using double colons
        // 関連関数はコロンを2つ挟んで呼び出される。
        p1: Point::origin(),
        p2: Point::new(3.0, 4.0),
    };

    // Methods are called using the dot operator
    // Note that the first argument `&self` is implicitly passed, i.e.
    // メソッドはドット演算子を用いて呼び出される。
    // 最初の引数`&self`は明示せずに受け渡されていることに注目。つまり
    // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
    println!("Rectangle perimeter: {}", rectangle.perimeter());
    println!("Rectangle area: {}", rectangle.area());

    let mut square = Rectangle {
        p1: Point::origin(),
        p2: Point::new(1.0, 1.0),
    };

    // Error! `rectangle` is immutable, but this method requires a mutable
    // object
    // エラー!`rectangle`はイミュータブルだがこのメソッドはミュータブルなオブジェクトを
    // 必要とする。
    //rectangle.translate(1.0, 0.0);
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。

    // Okay! Mutable objects can call mutable methods
    // OK! ミュータブルなオブジェクトはミュータブルなメソッドを呼び出せる。
    square.translate(1.0, 1.0);

    let pair = Pair(Box::new(1), Box::new(2));

    pair.destroy();

    // Error! Previous `destroy` call "consumed" `pair`
    // エラー!先ほどの`destroy`で`pair`はすでに消費されてしまっている。
    //pair.destroy();
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。
}

クロージャ

Rustにおけるクロージャは、その外側の環境を捕捉した関数のことです。例えば、次のコードは変数xを捕捉したクロージャです。

|val| val + x

クロージャの構文や機能は、その場限りの用途で何かを作るのに便利です。クロージャの呼び出しは関数の呼び出しと全く同じです。しかし、入力の型と戻り値の型は推論させることができますが、入力変数の名前は必ず指定しなくてはなりません。

クロージャの他の特徴を以下に示します。

  • 入力変数を囲むのに、()の代わりに||を用います。
  • 本体が単一の式の場合は、本体の区切り文字({})を省略できます。(それ以外の場合は必須です)
  • 外側の環境にある変数を捕捉することができます。
fn main() {
    // Increment via closures and functions.
    // 関数とクロージャのそれぞれで数値をインクリメントする
    fn function(i: i32) -> i32 { i + 1 }

    // Closures are anonymous, here we are binding them to references
    // Annotation is identical to function annotation but is optional
    // as are the `{}` wrapping the body. These nameless functions
    // are assigned to appropriately named variables.
    // 型アノテーションは、通常の関数と同様の方法で行えるが、必須ではない。
    // `{}`も必須ではない。
    // クロージャは一種の無名関数なので、適切な変数にバインディングしてやるとよい
    let closure_annotated = |i: i32| -> i32 { i + 1 };
    let closure_inferred  = |i     |          i + 1  ;

    let i = 1;
    // Call the function and closures.
    // 関数とクロージャを呼び出す。
    println!("function: {}", function(i));
    println!("closure_annotated: {}", closure_annotated(i));
    println!("closure_inferred: {}", closure_inferred(i));

    // A closure taking no arguments which returns an `i32`.
    // The return type is inferred.
    // 引数なしで`i32`を返すクロージャ。
    // 戻り値の型は推論された。
    let one = || 1;
    println!("closure returning one: {}", one());

}

要素の捕捉

クロージャはとてもフレキシブルに動作するように出来ています。クロージャにおいて型アノテーションをする必要が無いのは前述の仕組みのためですが、この仕組みのおかげでユースケースに応じて参照を取得したり値そのものを取得したりといった動作が可能になります。 クロージャは外側の環境にある要素を、以下の形で取得することができます。

  • リファレンス: &T
  • ミュータブルなリファレンス: &mut T
  • 値そのもの: T

クロージャは出来る限りリファレンスを取得しようとし、その他2つは必要なときのみ取得します。

fn main() {
    use std::mem;
    
    let color = String::from("green");

    // A closure to print `color` which immediately borrows (`&`) `color` and
    // stores the borrow and closure in the `print` variable. It will remain
    // borrowed until `print` is used the last time. 
    //
    // `println!` only requires arguments by immutable reference so it doesn't
    // impose anything more restrictive.
    // `color`をプリントするためのクロージャ。
    // これは`color`を借用(`&`)し、その借用とクロージャを`print`
    // という名の変数に保持する。
    // 借用は`print`がスコープから出るまで続く。
    // `println!`は参照を与えれば機能するので、これ以上なにかする必要はない。
    let print = || println!("`color`: {}", color);

    // Call the closure using the borrow.
    // 借用を行ったクロージャをコールする。
    print();

    // `color` can be borrowed immutably again, because the closure only holds
    // an immutable reference to `color`. 
    // `color`を再びイミュータブルで借用することができる。
    // これはクロージャが`color`に対するイミュータブルな参照しか保持しないからである。
    let _reborrow = &color;
    print();

    // A move or reborrow is allowed after the final use of `print`
    // 最後に`print`を使用した後は移動や再借用が許可される。
    let _color_moved = color;


    let mut count = 0;
    // A closure to increment `count` could take either `&mut count` or `count`
    // but `&mut count` is less restrictive so it takes that. Immediately
    // borrows `count`.
    //
    // A `mut` is required on `inc` because a `&mut` is stored inside. Thus,
    // calling the closure mutates the closure which requires a `mut`.
    // `count`をインクリメントするためのクロージャ。`count`と`&mut count`
    // の両方を取ることができるが、後者のほうが制限が少ないため、
    // (訳注: `count`だと`&mut count`と違い、一度しか呼ぶことができない。)
    // そちらを取る。直後に`count`を借用する。
    //
    // `inc`には`mut`をつける必要がある。なぜならミュータブルな型が
    // 中で使用されているからである。ミュータブルなクロージャは呼ぶたびに
    // 内部変数を変更する。
    let mut inc = || {
        count += 1;
        println!("`count`: {}", count);
    };

    // Call the closure using a mutable borrow.
    // クロージャを実行
    inc();

    // The closure still mutably borrows `count` because it is called later.
    // An attempt to reborrow will lead to an error.
    // クロージャはまだ `count` をミュータブルで借用している。
    // なぜなら後で呼ばれるからである。
    // 再借用しようとするとエラーになる。
    // let _reborrow = &count; 
    // ^ TODO: try uncommenting this line.
    // ^ TODO: この行のコメントアウトを解除しましょう。
    inc();

    // The closure no longer needs to borrow `&mut count`. Therefore, it is
    // possible to reborrow without an error
    // クロージャはもう`&mut count`を借用する必要がない。
    // なので、エラーを起こさず再借用することができる。
    let _count_reborrowed = &mut count; 

    
    // A non-copy type.
    // コピー不可能な型
    let movable = Box::new(3);

    // `mem::drop` requires `T` so this must take by value. A copy type
    // would copy into the closure leaving the original untouched.
    // A non-copy must move and so `movable` immediately moves into
    // the closure.
    // `mem::drop`は`T`(ジェネリック型)を取る必要があるため、このクロージャは
    // 参照ではなく値を取る。その場合、もしもコピー可能な値ならば、
    // 元の値はそのままでコピーのみを取る。不可能ならば値そのものを移動させる。
    let consume = || {
        println!("`movable`: {:?}", movable);
        mem::drop(movable);
    };

    // `consume` consumes the variable so this can only be called once.
    // `consume`は変数を消費(開放)するため、一度しか呼び出すことができない。
    consume();
    // consume();
    // ^ TODO: Try uncommenting this line.
    // ^ TODO: この行のコメントアウトを解除しましょう。
}

バーティカルパイプ(訳注:縦線記号||)の前にmoveを使用することで、キャプチャする変数の所有権を取ることをクロージャに強制します。

fn main() {
    // `Vec` has non-copy semantics.
    // `Vec`はコピーセマンティクスではない。
    let haystack = vec![1, 2, 3];

    let contains = move |needle| haystack.contains(needle);

    println!("{}", contains(&1));
    println!("{}", contains(&4));

    // println!("There're {} elements in vec", haystack.len());
    // ^ Uncommenting above line will result in compile-time error
    // because borrow checker doesn't allow re-using variable after it
    // has been moved.
    // ^ 上の行のコメントアウトを解除すると、コンパイル時エラーになる。
    // これは変数の所有権が移された後の再利用を借用チェッカーが許可しないからである。
    
    // Removing `move` from closure's signature will cause closure
    // to borrow _haystack_ variable immutably, hence _haystack_ is still
    // available and uncommenting above line will not cause an error.
    // クロージャのシグネチャから`move`を削除すると、クロージャは _haystack_ 変数を
    // イミュータブルで借用するようになる。
    // そのため _haystack_ はまだ利用可能となり、上の行のコメントアウトを解除しても
    // エラーが発生しなくなる。
}

参照

Box and std::mem::drop

捕捉時の型推論

Rustはたいていの場合、型アノテーションなしでも変数を捕捉する方法を臨機応変に選択してくれますが、関数を書く場合にはこの曖昧さは許されません。 引数のパラメータとしてクロージャを取る場合、そのクロージャの完全な型はいくつかのtraitsの中の1つを使って明示されなければなりません。 制限の少ない順に並べると、下記の通りです。

  • Fn: 参照(&T)によって捕捉するクロージャ
  • FnMut: ミュータブルな参照(&mut T)によって捕捉するクロージャ
  • FnOnce: 値(T)によって捕捉するクロージャ

変数ごとに、コンパイラは可能な限り制約の少ない方法でその変数を捕捉します。

例えば、FnOnceというアノテーションの付けられたパラメータを考えてみましょう。 これはそのクロージャが&T&mut TもしくはTどれか で捕捉することを指定するものですが、コンパイラは捕捉した変数がそのクロージャの中でどのように使用されるかに基づき、最終的に捕捉する方法を選択することになります。

これは、もし移動が可能であれば、いずれの種類の借用であっても同様に可能だからです。 その逆は正しくないことに注意してください。パラメータがFnとしてアノテーションされている場合、変数を&mut TTで捕捉することは許可されません。

以下の例では、FnFnMut、およびFnOnceを入れ替えて、何が起こるのかを見てみましょう。

// A function which takes a closure as an argument and calls it.
// <F> denotes that F is a "Generic type parameter"
fn apply<F>(f: F) where
    // The closure takes no input and returns nothing.
    // クロージャには引数も返り値もない。
    F: FnOnce() {
    // ^ TODO: Try changing this to `Fn` or `FnMut`.
    // ^ TODO: ここを`Fn`あるいは`FnMut`に変えてみましょう。

    f();
}

// A function which takes a closure and returns an `i32`.
// クロージャを引数に取り、`i32`を返す関数
fn apply_to_3<F>(f: F) -> i32 where
    // The closure takes an `i32` and returns an `i32`.
    // このクロージャは引数、返り値ともに`i32`
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    use std::mem;

    let greeting = "hello";
    // A non-copy type.
    // `to_owned` creates owned data from borrowed one
    // コピーではなくmoveが起きる型
    let mut farewell = "goodbye".to_owned();

    // Capture 2 variables: `greeting` by reference and
    // `farewell` by value.
    // 変数を2つ補足。`greeting`は参照を、
    // `farewell`は値をそれぞれ捕捉する。
    let diary = || {
        // `greeting` is by reference: requires `Fn`.
        // `greeting`は参照なので、`Fn`が必要。
        println!("I said {}.", greeting);

        // Mutation forces `farewell` to be captured by
        // mutable reference. Now requires `FnMut`.
        // `farewell`の値を変更するので、この時点で`FnMut`
        // が必要になる。
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // Manually calling drop forces `farewell` to
        // be captured by value. Now requires `FnOnce`.
        // `mem::drop`を明示的に呼ぶと`farewell`が値で
        // 捕捉される必要性が発生する。よって`FnOnce`が必要になる。
        mem::drop(farewell);
    };

    // Call the function which applies the closure.
    // クロージャを適用する関数を実行。
    apply(diary);

    // `double` satisfies `apply_to_3`'s trait bound
    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}

参照

std::mem::drop, Fn, FnMut, Generics, where and FnOnce

クロージャを受け取る関数

クロージャが周辺の環境から変数を取得するやり方は非常に明瞭です。何か注意すべき点はあるのでしょうか? もちろんです。関数内でクロージャを使う場合、[ジェネリック]型を使用する必要があります。詳しく見ていきましょう。


#![allow(unused)]
fn main() {
// `F` must be generic.
// `F` はジェネリック型でなくてはならない
fn apply<F>(f: F) where
    F: FnOnce() {
    f();
}
}

クロージャが定義されると、コンパイラは裏側で、無名の構造体を作り、そこにクロージャによって使用される外側の変数を入れます。同時にFnFnMutFnOnceという名のトレイトのいずれか一つを介してこの構造体に関数としての機能を実装し、実際に呼び出されるまで待ちます。

この無名構造体は型が未指定(unknown)なため、関数を実行する際にはジェネリクスが必要とされます。とはいえ、<T>で指定するだけでは、まだ曖昧です。(訳注: &self&mut selfselfのいずれをとるのかがわからないため)そのため、FnFnMutFnOnceのいずれか一つを実装することで対応しています。

// `F` must implement `Fn` for a closure which takes no
// inputs and returns nothing - exactly what is required
// for `print`.
// `F`は`Fn`を実装していなくてはならず、`Fn`は引数と返り値を持たない。
// `print`は文字をプリントするだけのクロージャなので、これが正しい。
fn apply<F>(f: F) where
    F: Fn() {
    f();
}

fn main() {
    let x = 7;

    // Capture `x` into an anonymous type and implement
    // `Fn` for it. Store it in `print`.
    // `x`を無名の構造体に入れ、それに対し`Fn`を実装する。
    // (訳注: ここでは`Fn`は`fn Fn(&self) -> {println!("{}", &self)}`)
    // その構造体を`print`にアサインする。
    let print = || println!("{}", x);

    apply(print);
}

参照

A thorough analysis, Fn, FnMut, and FnOnce

関数を受け取る関数

これまで、クロージャを引数として渡せることを見てきました。すると次の疑問が浮かんできます

「クロージャではない普通の関数を引数として渡すことは可能なのだろうか?」

可能です!もしパラメータとしてクロージャを取る関数を定義すれば、そのクロージャのトレイト境界を満たす任意の関数をパラメータとして渡すことができます。

// Define a function which takes a generic `F` argument
// bounded by `Fn`, and calls it
// 関数を引数として取り、即座に実行する関数を定義
fn call_me<F: Fn()>(f: F) {
    f();
}

// Define a wrapper function satisfying the `Fn` bound
// `Fn`境界を満たすラッパ関数を定義
fn function() {
    println!("I'm a function!");
}

fn main() {
    // Define a closure satisfying the `Fn` bound
    // `Fn`境界を満たすクロージャを定義
    let closure = || println!("I'm a closure!");

    call_me(closure);
    call_me(function);
}

クロージャによる変数の補足がどのように行われているかを詳しく見たいときはFnFnMutFnOnceを参照してください。

参照

Fn, FnMut, and FnOnce

クロージャを返す関数

クロージャを引数のパラメータとして用いることができるのと同様に、クロージャを戻り値として返すことも可能です。しかし無名のクロージャの型はその定義上、不明であるため、クロージャを返すためにはimpl Traitを使用する必要があります。

クロージャを返すために有効なトレイトは下記の通りです。

  • Fn
  • FnMut
  • FnOnce

更に、moveというキーワードを使用し、全ての捕捉が値でおこなわれることを明示しなければなりません。 これは、関数を抜けると同時に参照による捕捉がドロップされ、無効な参照がクロージャに残ってしまうのを防ぐためです。

fn create_fn() -> impl Fn() {
    let text = "Fn".to_owned();

    move || println!("This is a: {}", text)
}

fn create_fnmut() -> impl FnMut() {
    let text = "FnMut".to_owned();

    move || println!("This is a: {}", text)
}

fn create_fnonce() -> impl FnOnce() {
    let text = "FnOnce".to_owned();

    move || println!("This is a: {}", text)
}

fn main() {
    let fn_plain = create_fn();
    let mut fn_mut = create_fnmut();
    let fn_once = create_fnonce();

    fn_plain();
    fn_mut();
    fn_once();
}

参照

Fn, FnMut, ジェネリクス, impl Trait.

stdにおける使用例

この節ではstdライブラリを用いて、クロージャの利用例を幾つかお見せします。

Iterator::any

iterator::anyは、イテレータ内に一つでも条件を満たす要素があれば、trueを返し、さもなくばfalseを返すイテレータです。以下がそのシグネチャです

pub trait Iterator {
    // The type being iterated over.
    // イテレートされる値の型
    type Item;

    // `any` takes `&mut self` meaning the caller may be borrowed
    // and modified, but not consumed.
    // `any`は`&mut self`を取るため、イテレータを呼び出した値を借用し
    // 変更しますが、消費し尽くすことはありません。
    fn any<F>(&mut self, f: F) -> bool where
        // `FnMut` meaning any captured variable may at most be
        // modified, not consumed. `Self::Item` states it takes
        // arguments to the closure by value.
        // `FnMut`はクロージャによって補足される変数が変更される
        // 事はあっても消費されることはないということを示します。
        // `Self::Item`はクロージャが変数を値として取ることを示します。
        F: FnMut(Self::Item) -> bool {}
}
fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];

    // `iter()` for vecs yields `&i32`. Destructure to `i32`.
    // ベクトル型に対する`iter`は`&i32`を`yield`するので、`i32`へとデストラクト
    println!("2 in vec1: {}", vec1.iter()     .any(|&x| x == 2));
    // `into_iter()` for vecs yields `i32`. No destructuring required.
    // `into_iter()`の場合は`i32`を`yield`するので、デストラクトする必要はない。
    println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));

    let array1 = [1, 2, 3];
    let array2 = [4, 5, 6];

    // `iter()` for arrays yields `&i32`.
    // 配列に対する`iter()`は`&i32`をyieldする。
    println!("2 in array1: {}", array1.iter()     .any(|&x| x == 2));
    // `into_iter()` for arrays unusually yields `&i32`.
    // 配列に`into_iter()`を使うと例外的に`&i32`を`yield`する。
    println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
}

参照

std::iter::Iterator::any

Searching through iterators

Iterator::findはイテレータを辿る関数で、条件を満たす最初の値を探します。もし条件を満たす値がなければNoneを返します。型シグネチャは以下のようになります。

pub trait Iterator {
    // The type being iterated over.
    // イテレートされる値の型
    type Item;

    // `find` takes `&mut self` meaning the caller may be borrowed
    // and modified, but not consumed.
    // `find`は`&mut self`を取るため、イテレータを呼び出した値を借用し
    // 変更しますが、消費し尽くすことはありません。
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where
        // `FnMut` meaning any captured variable may at most be
        // modified, not consumed. `&Self::Item` states it takes
        // arguments to the closure by reference.
        // `FnMut`はクロージャによって補足される変数が変更される
        // 事はあっても消費されることはないということを示します。
        // `&Self::Item`はクロージャが変数を参照として取ることを示します。
        P: FnMut(&Self::Item) -> bool {}
}
fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];

    // `iter()` for vecs yields `&i32`.
    // ベクトル型に対する`iter`は`&i32`を`yield`する。
    let mut iter = vec1.iter();
    // `into_iter()` for vecs yields `i32`.
    // `inter_iter()`の場合は`i32`を`yield`する。
    let mut into_iter = vec2.into_iter();

    // `iter()` for vecs yields `&i32`, and we want to reference one of its
    // items, so we have to destructure `&&i32` to `i32`
    // `yield`された要素へのリファレンスは`&&i32`となる。`i32`へとデストラクトする。
    println!("Find 2 in vec1: {:?}", iter     .find(|&&x| x == 2));
    // `into_iter()` for vecs yields `i32`, and we want to reference one of
    // its items, so we have to destructure `&i32` to `i32`
    // `into_iter`の場合は`&i32`が要素のリファレンス。
    println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));

    let array1 = [1, 2, 3];
    let array2 = [4, 5, 6];

    // `iter()` for arrays yields `&i32`
    // 配列に対する`iter`も`&i32`を`yield`する。
    println!("Find 2 in array1: {:?}", array1.iter()     .find(|&&x| x == 2));
    // `into_iter()` for arrays unusually yields `&i32`
    // 配列に`into_iter()`を使うと例外的に`&i32`を`yield`する。
    println!("Find 2 in array2: {:?}", array2.into_iter().find(|&&x| x == 2));
}

Iterator::findは要素への参照を返します。 要素の インデックス を使用したい場合、Iterator::positionを使用してください。

fn main() {
    let vec = vec![1, 9, 3, 3, 13, 2];

    let index_of_first_even_number = vec.iter().position(|x| x % 2 == 0);
    assert_eq!(index_of_first_even_number, Some(5));
    
    
    let index_of_first_negative_number = vec.iter().position(|x| x < &0);
    assert_eq!(index_of_first_negative_number, None);
}

参照

std::iter::Iterator::find

std::iter::Iterator::find_map

std::iter::Iterator::position

std::iter::Iterator::rposition

高階関数

Rustには高階関数(Higher Order Functions, HOF)を扱う機能が備わっています。

fn is_odd(n: u32) -> bool {
    n % 2 == 1
}

fn main() {
    // 1000以下の奇数を2乗した値の合計を求める。
    println!("Find the sum of all the squared odd numbers under 1000");
    let upper = 1000;

    // Imperative approach
    // Declare accumulator variable
    // 宣言型プログラミングによるアプローチ
    // 値を蓄積する変数を宣言
    let mut acc = 0;
    // Iterate: 0, 1, 2, ... to infinity
    // 0から無限までイテレートする
    for n in 0.. {
        // Square the number
        // 値を2乗
        let n_squared = n * n;

        if n_squared >= upper {
            // Break loop if exceeded the upper limit
            // 上限に達した場合、ループを終了
            break;
        } else if is_odd(n_squared) {
            // Accumulate value, if it's odd
            // 奇数ならば値を値を足しあわせていく。
            acc += n_squared;
        }
    }
    println!("imperative style: {}", acc);

    // Functional approach
    // 関数型プログラミングによるアプローチ
    let sum_of_squared_odd_numbers: u32 =
        (0..).map(|n| n * n)                             // All natural numbers squared
                                                         // 全自然数を2乗し
             .take_while(|&n_squared| n_squared < upper) // Below upper limit
                                                         // そのうち上限より小さい値で
             .filter(|&n_squared| is_odd(n_squared))     // That are odd
                                                         // かつ奇数のものを
             .fold(0, |acc, n_squared| acc + n_squared); // Sum them
                                                         // 足し合わせる。
    println!("functional style: {}", sum_of_squared_odd_numbers);
}

オプション型イテレータには高階関数が使用されています。

Diverging functions

Diverging functions never return. They are marked using !, which is an empty type.


#![allow(unused)]
fn main() {
fn foo() -> ! {
    panic!("This call never returns.");
}
}

As opposed to all the other types, this one cannot be instantiated, because the set of all possible values this type can have is empty. Note that, it is different from the () type, which has exactly one possible value.

For example, this function returns as usual, although there is no information in the return value.

fn some_fn() {
    ()
}

fn main() {
    let a: () = some_fn();
    println!("This function returns and you can see this line.")
}

As opposed to this function, which will never return the control back to the caller.

#![feature(never_type)]

fn main() {
    let x: ! = panic!("This call never returns.");
    println!("You will never see this line!");
}

Although this might seem like an abstract concept, it is in fact very useful and often handy. The main advantage of this type is that it can be cast to any other one and therefore used at places where an exact type is required, for instance in match branches. This allows us to write code like this:

fn main() {
    fn sum_odd_numbers(up_to: u32) -> u32 {
        let mut acc = 0;
        for i in 0..up_to {
            // Notice that the return type of this match expression must be u32
            // because of the type of the "addition" variable.
            let addition: u32 = match i%2 == 1 {
                // The "i" variable is of type u32, which is perfectly fine.
                true => i,
                // On the other hand, the "continue" expression does not return
                // u32, but it is still fine, because it never returns and therefore
                // does not violate the type requirements of the match expression.
                false => continue,
            };
            acc += addition;
        }
        acc
    }
    println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
}

It is also the return type of functions that loop forever (e.g. loop {}) like network servers or functions that terminate the process (e.g. exit()).

モジュール

Rustにはコードを階層的に分割し、お互いの機能を隠蔽・公開するための強力なモジュールシステムが存在します。

モジュールは関数、構造体、トレイト、implブロック、さらには他のモジュールなどの要素の集合です。

プライベートとパブリック

デフォルトでは、モジュール内の要素はプライベートですが、これはpubで修飾することでパブリックな属性にすることができます。パブリックな属性のみがモジュールの外のスコープからアクセスすることができるようになります。

// A module named `my_mod`
// `my_mod`という名称のモジュール
mod my_mod {
    // Items in modules default to private visibility.
    // モジュール内の要素はデフォルトでプライベート
    fn private_function() {
        println!("called `my_mod::private_function()`");
    }

    // Use the `pub` modifier to override default visibility.
    // `pub`を用いてパブリックに変更
    pub fn function() {
        println!("called `my_mod::function()`");
    }

    // Items can access other items in the same module,
    // even when private.
    // モジュール内からならば、プライベートな属性にアクセスすることに支障はない。
    pub fn indirect_access() {
        print!("called `my_mod::indirect_access()`, that\n> ");
        private_function();
    }

    // Modules can also be nested
    pub mod nested {
        pub fn function() {
            println!("called `my_mod::nested::function()`");
        }

        #[allow(dead_code)]
        fn private_function() {
            println!("called `my_mod::nested::private_function()`");
        }

        // Functions declared using `pub(in path)` syntax are only visible
        // within the given path. `path` must be a parent or ancestor module
        pub(in crate::my_mod) fn public_function_in_my_mod() {
            print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> ");
            public_function_in_nested();
        }

        // Functions declared using `pub(self)` syntax are only visible within
        // the current module, which is the same as leaving them private
        pub(self) fn public_function_in_nested() {
            println!("called `my_mod::nested::public_function_in_nested()`");
        }

        // Functions declared using `pub(super)` syntax are only visible within
        // the parent module
        pub(super) fn public_function_in_super_mod() {
            println!("called `my_mod::nested::public_function_in_super_mod()`");
        }
    }

    pub fn call_public_function_in_my_mod() {
        print!("called `my_mod::call_public_function_in_my_mod()`, that\n> ");
        nested::public_function_in_my_mod();
        print!("> ");
        nested::public_function_in_super_mod();
    }

    // pub(crate) makes functions visible only within the current crate
    pub(crate) fn public_function_in_crate() {
        println!("called `my_mod::public_function_in_crate()`");
    }

    // Nested modules follow the same rules for visibility
    // ネストしたモジュールも、同様の性質を示す。
    mod private_nested {
        #[allow(dead_code)]
        pub fn function() {
            println!("called `my_mod::private_nested::function()`");
        }

        // Private parent items will still restrict the visibility of a child item,
        // even if it is declared as visible within a bigger scope.
        #[allow(dead_code)]
        pub(crate) fn restricted_function() {
            println!("called `my_mod::private_nested::restricted_function()`");
        }
    }
}

fn function() {
    println!("called `function()`");
}

fn main() {
    // Modules allow disambiguation between items that have the same name.
    // モジュールによって、同名の関数を区別することができる。
    function();
    my_mod::function();

    // Public items, including those inside nested modules, can be
    // accessed from outside the parent module.
    // パブリックな要素ならば、たとえネストしたものでも、
    // モジュールの外からアクセスすることができる。
    my_mod::indirect_access();
    my_mod::nested::function();
    my_mod::call_public_function_in_my_mod();

    // pub(crate) items can be called from anywhere in the same crate
    my_mod::public_function_in_crate();

    // pub(in path) items can only be called from within the module specified
    // Error! function `public_function_in_my_mod` is private
    //my_mod::nested::public_function_in_my_mod();
    // TODO ^ Try uncommenting this line

    // Private items of a module cannot be directly accessed, even if
    // nested in a public module:
    // プライベートな要素は、たとえパブリックなモジュール内に存在していても
    // 直接アクセスすることはできない。

    // Error! `private_function` is private
    // エラー!`private_function`はプライベート。
    //my_mod::private_function();
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにこの行をアンコメントしてみましょう。

    // Error! `private_function` is private
    // エラー!`private_function`はプライベート。
    //my_mod::nested::private_function();
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにこの行をアンコメントしてみましょう。

    // Error! `private_nested` is a private module
    // エラー!`private_nested`はプライベートなモジュール 。
    //my_mod::private_nested::function();
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにこの行をアンコメントしてみましょう。

    // Error! `private_nested` is a private module
    //my_mod::private_nested::restricted_function();
    // TODO ^ Try uncommenting this line
}

構造体の場合

構造体はそれ自身に加え、フィールドごとにもパブリック・プライベートを設定することができます。デフォルトではプライベートですが、pub宣言をすることで、フィールドをパブリックにすることができます。これは、構造体がモジュールの外から参照される時に限り意味のあるもので、情報の隠蔽(カプセル化)を達成するための機能です。

mod my {
    // A public struct with a public field of generic type `T`
    // パブリックなフィールド`T`(ジェネリック型)を持つパブリックな構造体
    pub struct OpenBox<T> {
        pub contents: T,
    }

    // A public struct with a private field of generic type `T`
    // プライベートなフィールド`T`(ジェネリック型)を持つパブリックな構造体
    #[allow(dead_code)]
    pub struct ClosedBox<T> {
        contents: T,
    }

    impl<T> ClosedBox<T> {
        // A public constructor method
        // パブリックなコンストラクタメソッドを持つ構造体
        pub fn new(contents: T) -> ClosedBox<T> {
            ClosedBox {
                contents: contents,
            }
        }
    }
}

fn main() {
    // Public structs with public fields can be constructed as usual
    // パブリックなフィールドを持つパブリックな構造体は、通常通り
    // インスタンス化できる。
    let open_box = my::OpenBox { contents: "public information" };

    // and their fields can be normally accessed.
    // フィールドにも普通にアクセスできる。
    println!("The open box contains: {}", open_box.contents);

    // Public structs with private fields cannot be constructed using field names.
    // Error! `ClosedBox` has private fields
    // プライベートなフィールドを持つ構造体は、インスタンス化する際に
    // フィールド名を指定することができない。
    // エラー!`ClosedBox`にはプライベートな属性が存在します。
    //let closed_box = my::ClosedBox { contents: "classified information" };
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにここをアンコメントしてみましょう。

    // However, structs with private fields can be created using
    // public constructors
    // そのような場合でも、パブリックなコンストラクタを介して作成
    // することは可能。
    let _closed_box = my::ClosedBox::new("classified information");

    // and the private fields of a public struct cannot be accessed.
    // Error! The `contents` field is private
    // たとえパブリックな構造体でも、プライベートなフィールドには
    // アクセス出来ない。
    // エラー!`contents`フィールドはプライベートです。
    //println!("The closed box contains: {}", _closed_box.contents);
    // TODO ^ Try uncommenting this line
    // TODO ^ ここをアンコメントしてみましょう。
}

参照

ジェネリック型, メソッド

use宣言

use宣言をすることで、要素の絶対パスを新しい名前にバインドすることができ、より簡潔な記述が可能になります。例えば以下のように使えます。

// extern crate deeply; // normally, this would exist and not be commented out!

use crate::deeply::nested::{
    my_first_function,
    my_second_function,
    AndATraitType
};

fn main() {
    my_first_function();
}

asキーワードを使用することで、インポートを別名にバインドすることができます。

// Bind the `deeply::nested::function` path to `other_function`.
// `deeply::nested::function`を`other_function`にバインド
use deeply::nested::function as other_function;

fn function() {
    println!("called `function()`");
}

mod deeply {
    pub mod nested {
        pub fn function() {
            println!("called `deeply::nested::function()`");
        }
    }
}

fn main() {
    // Easier access to `deeply::nested::function`
    // `deeply::nested::function`へ、より簡潔にアクセス
    other_function();

    println!("Entering block");
    {
        // This is equivalent to `use deeply::nested::function as function`.
        // This `function()` will shadow the outer one.
        // これは`use deeply::nested::function as function`と同等
        // この`function()`は外の`function()`をシャドウイングする
        use crate::deeply::nested::function;
        function();

        // `use` bindings have a local scope. In this case, the
        // shadowing of `function()` is only in this block.
        // `use`バインディングは局所的なスコープを持つ。
        // この場合には`function()`のシャドウイングはこのブロック内のみ
        function();

        println!("Leaving block");
    }

    function();
}

superself

super及びselfキーワードは、要素にアクセスする際に、曖昧さをなくし、不必要なハードコーディングを避けるために使用できます。

fn function() {
    println!("called `function()`");
}

mod cool {
    pub fn function() {
        println!("called `cool::function()`");
    }
}

mod my {
    fn function() {
        println!("called `my::function()`");
    }
    
    mod cool {
        pub fn function() {
            println!("called `my::cool::function()`");
        }
    }
    
    pub fn indirect_call() {
        // Let's access all the functions named `function` from this scope!
        // `function`という名の様々な関数をこのスコープ内から参照してみましょう。
        print!("called `my::indirect_call()`, that\n> ");
        
        // The `self` keyword refers to the current module scope - in this case `my`.
        // Calling `self::function()` and calling `function()` directly both give
        // the same result, because they refer to the same function.
        // `self`キーワードは現在のモジュールスコープを示す。この場合は`my`。
        // `self::function()`と`funcition()`は同じ関数であるため、同じ結果になる。
        self::function();
        function();
        
        // We can also use `self` to access another module inside `my`:
        // `my`以下の別のモジュールを呼び出す際に`self`を用いて明示的に参照できる。
        self::cool::function();
        
        // The `super` keyword refers to the parent scope (outside the `my` module).
        // `super`は親スコープ(`my`の外側)を参照する。
        super::function();
        
        // This will bind to the `cool::function` in the *crate* scope.
        // In this case the crate scope is the outermost scope.
        // 以下は *クレート* スコープ内の`cool::function`をバインディングする。
        // この場合、クレートスコープは一番外側のスコープである。
        {
            use crate::cool::function as root_function;
            root_function();
        }
    }
}

fn main() {
    my::indirect_call();
}

ファイルの階層構造

モジュールはファイル・ディレクトリ間の階層構造と対応関係にあります。モジュールにお互いがどのように見えているか、以下の様なファイルを例に詳しく見ていきましょう。

$ tree .
.
|-- my
|   |-- inaccessible.rs
|   |-- mod.rs
|   `-- nested.rs
`-- split.rs

In split.rs:

// This declaration will look for a file named `my.rs` or `my/mod.rs` and will
// insert its contents inside a module named `my` under this scope
// このように宣言すると、`my.rs`または、`my/mod.rs`という名のファイルを探し、
// その内容をこのファイル中で`my`という名から使用することができるようにします。
mod my;

fn function() {
    println!("called `function()`");
}

fn main() {
    my::function();

    function();

    my::indirect_access();

    my::nested::function();
}

In my/mod.rs:

// Similarly `mod inaccessible` and `mod nested` will locate the `nested.rs`
// and `inaccessible.rs` files and insert them here under their respective
// modules
// 同様に`mod inaccessible`、`mod nested`によって、`nested.rs`、`inaccessible.rs`の内容をこの中で使用することができるようになる。
// 訳注: `pub`をつけないかぎり、この中でしか使用できない。
mod inaccessible;
pub mod nested;

pub fn function() {
    println!("called `my::function()`");
}

fn private_function() {
    println!("called `my::private_function()`");
}

pub fn indirect_access() {
    print!("called `my::indirect_access()`, that\n> ");

    private_function();
}

In my/nested.rs:

pub fn function() {
    println!("called `my::nested::function()`");
}

#[allow(dead_code)]
fn private_function() {
    println!("called `my::nested::private_function()`");
}

In my/inaccessible.rs:

#[allow(dead_code)]
pub fn public_function() {
    println!("called `my::inaccessible::public_function()`");
}

では、以前と同じように実行できるか確認しましょう。

$ rustc split.rs && ./split
called `my::function()`
called `function()`
called `my::indirect_access()`, that
> called `my::private_function()`
called `my::nested::function()`

クレート

クレートはRustにおけるコンパイルの単位です。rustc some_file.rsが呼ばれると、some_file.rsは必ず クレートファイル として扱われます。もしsome_file.rsmod宣言を含んでいるのならば、コンパイルの 前に 、そのモジュールファイルの中身がmodの位置に挿入されます。言い換えると、それぞれのモジュールが独立にコンパイルされるということはありませんが、それぞれのクレートは互いに独立にコンパイルされるということです。

クレートはバイナリあるいはライブラリ形式でコンパイルされることが可能です。デフォルトではrustcはクレートからバイナリを作り出しますが、この振る舞いは--crate-typeフラグにlibを渡すことでオーバーライドできます。

ライブラリ

ではライブラリを作成し、それを別のクレートにリンクする方法を見ていきましょう。

pub fn public_function() {
    println!("called rary's `public_function()`");
}

fn private_function() {
    println!("called rary's `private_function()`");
}

pub fn indirect_access() {
    print!("called rary's `indirect_access()`, that\n> ");

    private_function();
}
$ rustc --crate-type=lib rary.rs
$ ls lib*
library.rlib

ライブラリは「lib」が頭につき、デフォルトでは、その後ろに元となったクレートファイル名をつけます。(訳注: ここではlib + rary)この振る舞いはcrate_nameアトリビュートを用いてオーバーライドできます。

Using a Library

To link a crate to this new library you may use rustc's --extern flag. All of its items will then be imported under a module named the same as the library. This module generally behaves the same way as any other module.

// extern crate rary; // May be required for Rust 2015 edition or earlier

fn main() {
    rary::public_function();

    // Error! `private_function` is private
    //rary::private_function();

    rary::indirect_access();
}
# Where library.rlib is the path to the compiled library, assumed that it's
# in the same directory here:
$ rustc executable.rs --extern rary=library.rlib --edition=2018 && ./executable 
called rary's `public_function()`
called rary's `indirect_access()`, that
> called rary's `private_function()`

Cargo

cargoはRustの公式パッケージ管理ツールです。とても便利な機能が多くあり、コードの品質や開発速度の向上に役立ちます。以下はその例です。

  • 依存関係の管理とcrates.io(Rustの公式パッケージレジストリ)とのインテグレーション
  • ユニットテスト
  • ベンチマーク

この章では、簡単な基本機能を説明します。包括的なドキュメントはThe Cargo Bookを参照してください。

依存関係

ほとんどのプログラムはライブラリに依存関係を持ちます。もし依存関係を手動で管理したことがあれば、それがどれだけ苦痛であるか分かるでしょう。幸運なことに、Rustのエコシステムにはcargoが標準装備されています!cargoによってプロジェクトの依存関係を管理することができます。

Rustのプロジェクトを新しく作るには下記のようにします。

# A binary
# バイナリ
cargo new foo

# OR A library
# またはライブラリ
cargo new --lib foo

この章の残りでは、ライブラリではなくバイナリを作ることを想定しますが、コンセプトはすべて同じです。

上のコマンドを実行すると、次のようなファイル階層ができます。

foo
├── Cargo.toml
└── src
    └── main.rs

main.rsがこの新規プロジェクトのルートのソースファイルです。なにも新しいことはありませんね。Cargo.tomlはこのプロジェクト(foo)のcargoの設定ファイルです。中を見てみるとこのようになっています。

[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]

[dependencies]

[package]の下のnameフィールドがプロジェクトの名前を決定します。これはクレートを公開するときにcrates.ioによって使われます(詳細は後述)。またコンパイルしたときの出力ファイルの名前でもあります。

versionフィールドはクレートのバージョン番号で、セマンティックバージョニングを使っています。

authorsフィールドは作者のリストで、クレートを公開するときに使われます。

[dependencies]セクションにはプロジェクトの依存関係を追加できます。

例えば、プログラムに素晴らしいCLIが欲しいとします。crates.io(Rustの公式パッケージレジストリ)には素晴らしいパッケージがたくさんあります。よくある選択肢の1つはclapです。この記事を書いている時点でのclapの最新の公開バージョンは2.27.1です。依存関係をプログラムに追加するには、Cargo.toml[dependencies]の下にclap = "2.27.1"と単に書き加えます。これだけです!clapをプログラム内で使用できます。

cargo他の形式の依存関係もサポートしています。その一部を示します。

[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]

[dependencies]
clap = "2.27.1" # from crates.io
                # crates.ioから
rand = { git = "https://github.com/rust-lang-nursery/rand" } # from online repo
                                                             # オンラインのレポジトリから
bar = { path = "../bar" } # from a path in the local filesystem
                          # ローカルのファイルシステムのパスから

cargoは依存管理ツール以上のこともできます。Cargo.tomlformat specificationに全ての設定オプションがリストアップされています。

プロジェクトをビルドするには、プロジェクトディレクトリのどこか(サブディレクトでも!)でcargo buildを実行します。またcargo runでビルドと実行をできます。これらのコマンドは、全ての依存関係の解決、必要なクレートのダウンロード、自分のクレートを含む全てのビルドを行うことに注意してください。(makeと同様、まだビルドしていないものだけをビルドします。)

Voila!これで完成です!

慣例

前の章ではこのようなディレクトリ階層がありました。

foo
├── Cargo.toml
└── src
    └── main.rs

しかし同じプロジェクトで2つのバイナリが欲しいとします。その場合は?

cargoはこれもサポートしています。以前見た通りデフォルトのバイナリ名はmainですが、bin/ディレクトリに置くことで他のバイナリを追加できます。

foo
├── Cargo.toml
└── src
    ├── main.rs
    └── bin
        └── my_other_bin.rs

デフォルトバイナリや他のバイナリではなく、このバイナリをコンパイルや実行するようにcargoに伝えるには、cargo--bin my_other_binフラグを渡します。ここではmy_other_binが対象のバイナリの名前です。

バイナリの追加に加えて、cargoはベンチマークやテスト、サンプルなどのその他の機能もサポートしています。

次の章ではテストについてより詳しく見ていきます。

テスト

知っての通り、テストはどんなソフトウェアにも不可欠です!Rustはユニットテストと統合テストを第一級にサポートしています(TRPLのこの章を参照してください)。

上のリンク先のテストの章では、ユニットテストと統合テストの書き方を紹介しています。ユニットテストはテスト対象のモジュール内に、統合テストはtests/ディレクトリ内に置きます。

foo
├── Cargo.toml
├── src
│   └── main.rs
│   └── lib.rs
└── tests
    ├── my_test.rs
    └── my_other_test.rs

tests内の各ファイルは個別の統合テストです。 これはライブラリを依存クレートから呼ばれたかのようにテストできます。

The Testing chapter elaborates on the three different testing styles: Unit, Doc, and Integration.

cargoは、全てのテストを簡単に実行する方法を提供します。

$ cargo test

出力はこのようになります。

$ cargo test
   Compiling blah v0.1.0 (file:///nobackup/blah)
    Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs
     Running target/debug/deps/blah-d3b32b97275ec472

running 3 tests
test test_bar ... ok
test test_baz ... ok
test test_foo_bar ... ok
test test_foo ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

パターンにマッチする名前のテストを実行することもできます。

$ cargo test test_foo
$ cargo test test_foo
   Compiling blah v0.1.0 (file:///nobackup/blah)
    Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
     Running target/debug/deps/blah-d3b32b97275ec472

running 2 tests
test test_foo ... ok
test test_foo_bar ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out

注意:Cargoは複数のテストを並列で実行することがありますので、それらが互いに競合しないようにしてください。

One example of this concurrency causing issues is if two tests output to a file, such as below:


#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    // Import the necessary modules
    use std::fs::OpenOptions;
    use std::io::Write;

    // This test writes to a file
    #[test]
    fn test_file() {
        // Opens the file ferris.txt or creates one if it doesn't exist.
        let mut file = OpenOptions::new()
            .append(true)
            .create(true)
            .open("ferris.txt")
            .expect("Failed to open ferris.txt");

        // Print "Ferris" 5 times.
        for _ in 0..5 {
            file.write_all("Ferris\n".as_bytes())
                .expect("Could not write to ferris.txt");
        }
    }

    // This test tries to write to the same file
    #[test]
    fn test_file_also() {
        // Opens the file ferris.txt or creates one if it doesn't exist.
        let mut file = OpenOptions::new()
            .append(true)
            .create(true)
            .open("ferris.txt")
            .expect("Failed to open ferris.txt");

        // Print "Corro" 5 times.
        for _ in 0..5 {
            file.write_all("Corro\n".as_bytes())
                .expect("Could not write to ferris.txt");
        }
    }
}
}

Although the intent is to get the following:

$ cat ferris.txt
Ferris
Ferris
Ferris
Ferris
Ferris
Corro
Corro
Corro
Corro
Corro

What actually gets put into ferris.txt is this:

$ cargo test test_foo
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris
Corro
Ferris

ビルドスクリプト

cargoによる通常のビルドでは十分でないことが時々あります。コード生成や、コンパイルが必要なネイティブコードなど、cargoがクレートをうまくコンパイルするにはなんらかの前提条件が必要かもしれません。この問題を解決するため、Cargoが実行できるビルドスクリプトがあります。

ビルドスクリプトをパッケージに追加するには、以下のようにCargo.tomlの中で指定できます。

[package]
...
build = "build.rs"

それ以外の場合、Cargoはデフォルトでプロジェクトディレクトリからbuild.rsを探します。

ビルドスクリプトの使い方

ビルドスクリプトは単にRustのファイルの1つで、パッケージ内の他のファイルをコンパイルする前にコンパイルされて起動されます。そのため、クレートの前提条件を満たすために使用できます。

Cargoは、ここで指定された環境変数を介してスクリプトに入力を与えます。

スクリプトは標準出力に出力します。出力される行は全て、target/debug/build/<pkg>/outputに書き込まれます。さらに、行頭にcargo:がついた行はCargoに直接解釈されるため、パッケージのコンパイル時のパラメーターを定義するのに使用できます。

より詳細な仕様や例については、Cargo specificationを参照してください。

アトリビュート

アトリビュートはモジュール、クレート、要素に対するメタデータです。以下がその使用目的です。

アトリビュートがクレート全体に適用される場合、#![crate_attribute]という書き方になります。モジュールないしは要素に適用される場合は#[item_attribute]になります。(!がないことに注目)

アトリビュートは以下の様な書き方で引数を取ることができます。

  • #[attribute = "value"]
  • #[attribute(key = "value")]
  • #[attribute(value)]

Attributes can have multiple values and can be separated over multiple lines, too:

#[attribute(value, value2)]


#[attribute(value, value2, value3,
            value4, value5)]

dead_code

コンパイラはdead_codeと呼ばれるリント機能を持つため、使用されていない関数が存在するときに警告を出します。 アトリビュート によってこの機能を無効化することができます。

fn used_function() {}

// `#[allow(dead_code)]` is an attribute that disables the `dead_code` lint
// `#[allow(dead_code)]`は`dead_code`リントを抑制するアトリビュートです。
#[allow(dead_code)]
fn unused_function() {}

fn noisy_unused_function() {}
// FIXME ^ Add an attribute to suppress the warning
// FIXME ^ 警告を抑制するアトリビュートを追加しましょう。

fn main() {
    used_function();
}

実際のコード中では、使用されていないコードが有る場合はそれを除外するべきです。この文書中では随所でアトリビュートによって警告を抑制していますが、それはあくまでインタラクティブな例を皆さんに提供するためです。

クレート

crate_typeアトリビュートは、そのクレートがライブラリ、バイナリのいずれにコンパイルされるべきかをコンパイラに伝えるために使用します。ライブラリの場合は、どのタイプのライブラリであるかも伝えることができます。crate_nameはクレートの名前を決定するのに使用します。

However, it is important to note that both the crate_type and crate_name attributes have no effect whatsoever when using Cargo, the Rust package manager. Since Cargo is used for the majority of Rust projects, this means real-world uses of crate_type and crate_name are relatively limited.

// This crate is a library
// このクレートはライブラリである。
#![crate_type = "lib"]
// The library is named "rary"
// このライブラリの名前は「rary」である。
#![crate_name = "rary"]

pub fn public_function() {
    println!("called rary's `public_function()`");
}

fn private_function() {
    println!("called rary's `private_function()`");
}

pub fn indirect_access() {
    print!("called rary's `indirect_access()`, that\n> ");

    private_function();
}

crate_typeアトリビュートが使用されているときは、rustc--crate-typeフラグを伝える必要はありません。

$ rustc lib.rs
$ ls lib*
library.rlib

cfg

環境に応じたコンパイルをするには2種類の方法があります。

  • cfgアトリビュート: #[cfg(...)]をアトリビュートとして使用する。
  • cfg!マクロ: cfg!(...)をブーリアンとして評価する。

前者は条件付きコンパイルを行いますが、後者はtrueまたはfalseリテラルに評価され実行時にチェックすることが可能です。 いずれの場合も適切なシンタックスで記述する必要があります。

// This function only gets compiled if the target OS is linux
// この関数はターゲットOSがLinuxの時のみコンパイルされる。
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
    println!("You are running linux!");
}

// And this function only gets compiled if the target OS is *not* linux
// そしてこの関数はターゲットOSがLinux *ではない* ときのみコンパイルされる。
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
    println!("You are *not* running linux!");
}

fn main() {
    are_you_on_linux();

    println!("Are you sure?");
    if cfg!(target_os = "linux") {
        println!("Yes. It's definitely linux!");
    } else {
        println!("Yes. It's definitely *not* linux!");
    }
}

参照

参照(reference), cfg!, マクロ.

条件の追加

target_osのように、いくつかの条件分岐はrustcが暗黙のうちに提供しています。条件を独自に追加する場合には--cfgフラグを用いてrustcに伝える必要があります。

#[cfg(some_condition)]
fn conditional_function() {
    println!("condition met!");
}

fn main() {
    conditional_function();
}

Try to run this to see what happens without the custom cfg flag.

cfgフラグがある場合:

$ rustc --cfg some_condition custom.rs && ./custom
condition met!

ジェネリクス

ジェネリクスとは、型と関数の機能をより汎用的に使えるようにするための機能です。これはあらゆる局面でコードの重複を避けるために非常に役立ちますが、多少構文が複雑になります。すなわち、ジェネリック型を使いこなすには、どのようなジェネリック型がきちんと機能するかに細心の注意を払う必要があります。 The simplest and most common use of generics is for type parameters.

ジェネリック型の型パラメータにはかぎ括弧(angle brackets)とアッパーキャメルケース(camel case)が使われます。: <Aaa, Bbb, ...>ジェネリックな型パラメータはたいていの場合<T>で示されます。Rustの場合、「ジェネリクス」には「1つ以上のジェネリックな型パラメータ<T>を受け付けるもの」という意味もあります。ジェネリックな型パラメータを指定された場合、それは必ずジェネリック型になり、そうでなければ必ず非ジェネリック型、すなわち具象型(concrete)になります。

例として、あらゆる型の引数Tをとる ジェネリック関数 fooを定義すると:

fn foo<T>(arg: T) { ... }

となります。Tはジェネリックな型パラメータに指定されているので、この場所で(arg: T)のように使用するとジェネリック型として扱われます。これはTという構造体がそれ以前に定義されていても同様です。

では、手を動かしながらジェネリック型の構文を体験していきましょう。

// A concrete type `A`.
// `A`という具象型
struct A;

// In defining the type `Single`, the first use of `A` is not preceded by `<A>`.
// Therefore, `Single` is a concrete type, and `A` is defined as above.
// `Single`という型を定義する際に`A`を使用しているが、その最初の使用よりも
// 先に`<A>`がないため、また、`A`自身も具象型であるため、`Single`は具象型となる。
struct Single(A);
//            ^ Here is `Single`s first use of the type `A`.
//            ^ Singleによる`A`の一番最初の使用はここ

// Here, `<T>` precedes the first use of `T`, so `SingleGen` is a generic type.
// Because the type parameter `T` is generic, it could be anything, including
// the concrete type `A` defined at the top.
// ここでは`<T>`が一番初めの`T`の使用よりも先に来ている。よって`SingleGen`はジェネリック型
// となる。なぜならば型パラメータ`T`がジェネリックだからである。`T`はどんな型にもなりえるため、
// 上で定義した`A`を受け取ることもできる。
struct SingleGen<T>(T);

fn main() {
    // `Single` is concrete and explicitly takes `A`.
    // `Single`は具象型で、`A`のみを受け取る。
    let _s = Single(A);
    
    // Create a variable `_char` of type `SingleGen<char>`
    // and give it the value `SingleGen('a')`.
    // Here, `SingleGen` has a type parameter explicitly specified.
    // `_char`という名の変数を生成する。これは`SingleGen<char>`
    // という型で、値は`SingleGen('a')`となる。ここでは、`SingleGen`には明示的な型パラメータ
    // が与えられている。
    let _char: SingleGen<char> = SingleGen('a');

    // `SingleGen` can also have a type parameter implicitly specified:
    // `SingleGen`型の変数には明示的に型パラメータを与えなくてもよい。
    let _t    = SingleGen(A); // Uses `A` defined at the top.
                              // 上で定義した`A`を使用
    let _i32  = SingleGen(6); // Uses `i32`.
                              // `i32`を使用
    let _char = SingleGen('a'); // Uses `char`.
                                // `char`を使用
}

参照

構造体

関数

「型Tはその前に<T>があるとジェネリック型になる」というルールは関数に対しても当てはまります。

ジェネリック関数を使用する際、以下の様な場合には型パラメータを明示する必要があります。

  • 返り値がジェネリック型である場合。
  • コンパイラが型パラメータを推論するのに十分な情報がない場合

型パラメータを明示したうえでの関数呼び出しの構文はfun::<A, B, ...>()のようになります。

struct A;          // Concrete type `A`.
                   // 具象型`A`.
struct S(A);       // Concrete type `S`.
                   // 具象型`S`.
struct SGen<T>(T); // Generic type `SGen`.
                   // ジェネリック型`SGen`.

// The following functions all take ownership of the variable passed into
// them and immediately go out of scope, freeing the variable.
// 以下の関数は全て変数の所有権をとった後すぐにスコープを抜けて
// 変数をメモリ上から開放する。

// Define a function `reg_fn` that takes an argument `_s` of type `S`.
// This has no `<T>` so this is not a generic function.
// `S`という型の引数`_s`をとる`reg_fn`という関数を定義
// `<T>`がないのでジェネリック関数ではない
fn reg_fn(_s: S) {}

// Define a function `gen_spec_t` that takes an argument `_s` of type `SGen<T>`.
// It has been explicitly given the type parameter `A`, but because `A` has not 
// been specified as a generic type parameter for `gen_spec_t`, it is not generic.
// `gen_spec_t`という関数を定義。これは`A`という型を与えられた`Sgen<T>`
// という型の引数`_s`を取る。関数名の直後に`<A>`という型パラメータでAが
// ジェネリックであることを明示していないので、この関数はAをジェネリック型
// としては取らない
fn gen_spec_t(_s: SGen<A>) {}

// Define a function `gen_spec_i32` that takes an argument `_s` of type `SGen<i32>`.
// It has been explicitly given the type parameter `i32`, which is a specific type.
// Because `i32` is not a generic type, this function is also not generic.
// `gen_spec_i32`という関数を定義。
// これは明示的な型パラメータとして`i32`を与えられた`Sgen<i32>`型の引数`_s`をとる
// この関数もジェネリックではない
fn gen_spec_i32(_s: SGen<i32>) {}

// Define a function `generic` that takes an argument `_s` of type `SGen<T>`.
// Because `SGen<T>` is preceded by `<T>`, this function is generic over `T`.
// `generic`という関数を定義。`SGen<T>`という型の引数`_s`を取る。`<T>`が`SGen<T>`に
// 先行しているため、これはTに対してジェネリックな関数
fn generic<T>(_s: SGen<T>) {}

fn main() {
    // Using the non-generic functions
    // ジェネリックでない関数を使用する
    reg_fn(S(A));          // Concrete type.
                           // 具象型
    gen_spec_t(SGen(A));   // Implicitly specified type parameter `A`.
                           // 型パラメータ`A`を暗黙のうちに受け取る
    gen_spec_i32(SGen(6)); // Implicitly specified type parameter `i32`.
                           // 型パラメータ`i32`を暗黙のうちに受け取る

    // Explicitly specified type parameter `char` to `generic()`.
    generic::<char>(SGen('a'));

    // Implicitly specified type parameter `char` to `generic()`.
    // 型パラメータ`char`を暗黙的に`generic()`に渡す
    generic(SGen('c'));
}

参照

関数, 構造体

メソッド

関数と同様、implでメソッドを実装する際にもジェネリック型特有の記法が必要です。


#![allow(unused)]
fn main() {
struct S; // Concrete type `S`
          // 具象型`S`
struct GenericVal<T>(T); // Generic type `GenericVal`
                         // ジェネリック型`GenericVal`

// impl of GenericVal where we explicitly specify type parameters:
// 型パラメータを指定したうえで、GenericValにメソッドを実装
impl GenericVal<f32> {} // Specify `f32`
                        // `f32`の場合のメソッド
impl GenericVal<S> {} // Specify `S` as defined above
                      // 上で定義した`S`への実装

// `<T>` Must precede the type to remain generic
// ジェネリック型のまま扱うには`<T>`が先に来る必要がある。
impl<T> GenericVal<T> {}
}
struct Val {
    val: f64,
}

struct GenVal<T> {
    gen_val: T,
}

// impl of Val
// Valに対してimpl
impl Val {
    fn value(&self) -> &f64 {
        &self.val
    }
}

// impl of GenVal for a generic type `T`
// ジェネリック型`T`の場合のメソッドをGenValに対して実装
impl<T> GenVal<T> {
    fn value(&self) -> &T {
        &self.gen_val
    }
}

fn main() {
    let x = Val { val: 3.0 };
    let y = GenVal { gen_val: 3i32 };

    println!("{}, {}", x.value(), y.value());
}

参照

参照を返す関数, impl, struct

ジェネリックトレイト

もちろんトレイトもジェネリクスを活用することができます。ここではDropトレイトをジェネリックメソッドとして再実装し、自身と引数として受け取った値の両方をdropするようなメソッドにします。

// Non-copyable types.
// コピー不可な型
// 訳注: `clone()`メソッドを用いないかぎり、値のコピーではなくムーブが起きる型
struct Empty;
struct Null;

// A trait generic over `T`.
// ジェネリック型 `T`に対するトレイト
trait DoubleDrop<T> {
    // Define a method on the caller type which takes an
    // additional single parameter `T` and does nothing with it.
    // `self`に加えてもう一つジェネリック型を受け取り、
    // 何もしないメソッドのシグネチャを定義
    fn double_drop(self, _: T);
}

// Implement `DoubleDrop<T>` for any generic parameter `T` and
// caller `U`.
// `U`を`self`として、`T`をもう一つの引数として受け取る`DoubleDrop<T>`
// を実装する。`U`,`T`はいずれもジェネリック型
impl<T, U> DoubleDrop<T> for U {
    // This method takes ownership of both passed arguments,
    // deallocating both.
    // このメソッドは2つの引数の所有権を取り、メモリ上から開放する。
    fn double_drop(self, _: T) {}
}

fn main() {
    let empty = Empty;
    let null  = Null;

    // Deallocate `empty` and `null`.
    // `empty`と`null`を開放
    empty.double_drop(null);

    //empty;
    //null;
    // ^ TODO: Try uncommenting these lines.
    // ^ TODO: これらの行をアンコメントしてみましょう。
}

参照

Drop, 構造体(struct), トレイト(trait)

ジェネリック境界

ジェネリックプログラミングをしていると、型パラメータが特定の機能を持っていることを規定するため、トレイトに境界(bound)を設ける必要があることがよくあります。例えば、以下の例では、引数のDisplayトレイトを用いてプリントを行うため、TDisplayを持っていることを規定しています。つまり、「TDisplayを実装 していなくてはならない 」という意味です。

// Define a function `printer` that takes a generic type `T` which
// must implement trait `Display`.
// `Display`トレイトを実装している`T`を引数として取る
// `printer`という関数を定義。
fn printer<T: Display>(t: T) {
    println!("{}", t);
}

境界は、ジェネリクスを全ての型ではなく、一定条件を満たす型に対してのみ適用するためにあります。つまり

訳注: <T: Display><T>の部分集合であることを意識すると、「境界」という言葉の意味がしっくり来ると思います。

struct S<T: Display>(T);

// Error! `Vec<T>` does not implement `Display`. This
// specialization will fail.
// エラー! `Vec<T>`は`Display`を実装していないため、この特殊化
// は失敗します。
let s = S(vec![1]);

境界のもう一つの効果は、ジェネリック型のインスタンスが、境界条件となるトレイトのメソッドにアクセスすることができるようになる点です。以下がその例です。

// A trait which implements the print marker: `{:?}`.
// プリント時のマーカー`{:?}`を実装するトレイト
use std::fmt::Debug;

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 { self.length * self.height }
}

#[derive(Debug)]
struct Rectangle { length: f64, height: f64 }
#[allow(dead_code)]
struct Triangle  { length: f64, height: f64 }

// The generic `T` must implement `Debug`. Regardless
// of the type, this will work properly.
// ジェネリック型`T`は`Debug`トレイトを実装していなくてはならない。
// その限りにおいて、`T`がどのような具象型であろうとも次の関数は動作する。
fn print_debug<T: Debug>(t: &T) {
    println!("{:?}", t);
}

// `T` must implement `HasArea`. Any type which meets
// the bound can access `HasArea`'s function `area`.
// 「`T`は`HasArea`を実装していなくてはならない」という境界条件を
// 満たしていれば、`HasArea`の関数`area`にアクセスできる。
fn area<T: HasArea>(t: &T) -> f64 { t.area() }

fn main() {
    let rectangle = Rectangle { length: 3.0, height: 4.0 };
    let _triangle = Triangle  { length: 3.0, height: 4.0 };

    print_debug(&rectangle);
    println!("Area: {}", area(&rectangle));

    //print_debug(&_triangle);
    //println!("Area: {}", area(&_triangle));
    // ^ TODO: Try uncommenting these.
    // | Error: Does not implement either `Debug` or `HasArea`. 
    // ^ TODO: これらの行をアンコメントしてみましょう。
    // | Error: `Debug` も `HasArea`もどちらも実装されていません!
}

付け加えておくと、where句を用いて境界を適用することもできます。場合によってはこちらの記法を使用したほうが読みやすくなる場合もあります。

参照

std::fmt, 構造体(struct), トレイト

テストケース: 空トレイト

トレイト境界の仕組みから、「トレイトがなにも機能を持っていなくとも境界条件として使用できることには変わりはない」という帰結がもたらされます。EqCopystdライブラリにおけるそのような例です。

struct Cardinal;
struct BlueJay;
struct Turkey;

trait Red {}
trait Blue {}

impl Red for Cardinal {}
impl Blue for BlueJay {}

// These functions are only valid for types which implement these
// traits. The fact that the traits are empty is irrelevant.
// 以下の関数はトレイト境界を設けているが、そのトレイトが空である
// か否かとは関係がない。
fn red<T: Red>(_: &T)   -> &'static str { "red" }
fn blue<T: Blue>(_: &T) -> &'static str { "blue" }

fn main() {
    // 訳注: 以下は全て鳥の名前
    // 猩々紅冠鳥
    let cardinal = Cardinal;
    // アオカケス
    let blue_jay = BlueJay;
    // 七面鳥
    let _turkey   = Turkey;

    // `red()` won't work on a blue jay nor vice versa
    // because of the bounds.
    // トレイト境界のため、`red`は`blue_jay`に対しては使用できない。
    // `blue`と`Cardinal`も同様、
    println!("A cardinal is {}", red(&cardinal));
    println!("A blue jay is {}", blue(&blue_jay));
    //println!("A turkey is {}", red(&_turkey));
    // ^ TODO: Try uncommenting this line.
    // ^ TODO: この行をアンコメントしてみましょう。
}

参照

std::cmp::Eq, [std::cmp::Ords][ord], トレイト

複数のジェネリック境界

+を用いて1つの型に複数のトレイト境界を設けることができます。複数の引数を受け取るときは、通常時と同様、,で区切ります。

use std::fmt::{Debug, Display};

fn compare_prints<T: Debug + Display>(t: &T) {
    println!("Debug: `{:?}`", t);
    println!("Display: `{}`", t);
}

fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
    println!("t: `{:?}`", t);
    println!("u: `{:?}`", u);
}

fn main() {
    let string = "words";
    let array = [1, 2, 3];
    let vec = vec![1, 2, 3];

    compare_prints(&string);
    //compare_prints(&array);
    // TODO ^ Try uncommenting this.
    // TODO ^ ここをアンコメントしてみましょう。

    compare_types(&array, &vec);
}

参照

std::fmt, トレイト

Where句

トレイト境界は、{の直前にwhere句を導入することでも設けることができます。whereはさらに、型パラメータだけでなく任意の型に対してのみ適用できます。

where句のほうが有効なケースには例えば

  • ジェネリック型とジェネリック境界に別々に制限を加えたほうが明瞭になる場合 つまり、
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}

// Expressing bounds with a `where` clause
// `where`を用いてジェネリック境界を設ける。
impl <A, D> MyTrait<A, D> for YourType where
    A: TraitB + TraitC,
    D: TraitE + TraitF {}
  • where句の方が通常の構文より表現力が高い場合

があります。

use std::fmt::Debug;

trait PrintInOption {
    fn print_in_option(self);
}

// Because we would otherwise have to express this as `T: Debug` or 
// use another method of indirect approach, this requires a `where` clause:
// `where`句を用いない場合、以下と等価な機能を実装するには、
// `<T: Debug>`という形で表現するか、別の直接的でない方法
// を使用するかしなくてはならない。
impl<T> PrintInOption for T where
    Option<T>: Debug {
    // We want `Option<T>: Debug` as our bound because that is what's
    // being printed. Doing otherwise would be using the wrong bound.
    // プリントされるのが`Some(self)`であるため、この関数の
    // ジェネリック境界として`Option<T>: Debug`を使用したい。
    fn print_in_option(self) {
        println!("{:?}", Some(self));
    }
}

fn main() {
    let vec = vec![1, 2, 3];

    vec.print_in_option();
}

参照

RFC, 構造体, トレイト, エラーハンドリングの日本語による解説記事

New Type Idiom

The newtype idiom gives compile time guarantees that the right type of value is supplied to a program.

For example, an age verification function that checks age in years, must be given a value of type Years.

struct Years(i64);

struct Days(i64);

impl Years {
    pub fn to_days(&self) -> Days {
        Days(self.0 * 365)
    }
}


impl Days {
    /// truncates partial years
    pub fn to_years(&self) -> Years {
        Years(self.0 / 365)
    }
}

fn old_enough(age: &Years) -> bool {
    age.0 >= 18
}

fn main() {
    let age = Years(5);
    let age_days = age.to_days();
    println!("Old enough {}", old_enough(&age));
    println!("Old enough {}", old_enough(&age_days.to_years()));
    // println!("Old enough {}", old_enough(&age_days));
}

Uncomment the last print statement to observe that the type supplied must be Years.

To obtain the newtype's value as the base type, you may use the tuple or destructuring syntax like so:

struct Years(i64);

fn main() {
    let years = Years(42);
    let years_as_primitive_1: i64 = years.0; // Tuple
    let Years(years_as_primitive_2) = years; // Destructuring
}

参照

structs

関連型

関連要素(Associated Items)とは複数の型の要素(items)に関係のある規則の総称です。トレイトの拡張機能であり、トレイトの中で新しい要素を定義することを可能にします。

そのように定義する要素の一つに 関連型 があります。これにより、ジェネリックなコンテナ型に対するトレイトを使用する際に、よりシンプルな書き方ができるようになります。

参照

RFC

関連型が必要になる状況

コンテナ型に、その要素に対してジェネリックなトレイトを実装した場合、そのトレイトを使用する者は全てのジェネリック型を明記 しなくてはなりません

以下の例ではContainsトレイトはジェネリック型ABの使用を許しています。その後、Container型に対してContainsを実装していますが、その際後にfn difference()が使用できるように、ABはそれぞれi32と明記されています。

Containsはジェネリックトレイトなので、fn difference()では 全ての ジェネリック型を宣言しなくてはなりません。実際のところ、AB引数 であるCによって決定されていて欲しいにも関わらず、です。これは次のページで紹介する関連型と呼ばれる機能によって可能です。

struct Container(i32, i32);

// A trait which checks if 2 items are stored inside of container.
// Also retrieves first or last value.
// 2つの要素がコンテナ型の中にあることをチェックするトレイト
// また、最初と最後の値を取得することもできる
trait Contains<A, B> {
    fn contains(&self, _: &A, _: &B) -> bool; // Explicitly requires `A` and `B`.
                                              // `A`と`B`の両方を明示的に要求する
    fn first(&self) -> i32; // Doesn't explicitly require `A` or `B`.
                            // `A`、`B`いずれも要求しない
    fn last(&self) -> i32;  // Doesn't explicitly require `A` or `B`.
                            // `A`、`B`いずれも要求しない
}

impl Contains<i32, i32> for Container {
    // True if the numbers stored are equal.
    // コンテナ内の2つの要素が等しければTrueを返す
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }

    // Grab the first number.
    // ひとつ目の値を取得
    fn first(&self) -> i32 { self.0 }

    // Grab the last number.
    // 最後(2つめ)の値を取得
    fn last(&self) -> i32 { self.1 }
}

// `C` contains `A` and `B`. In light of that, having to express `A` and
// `B` again is a nuisance.
// `A`と`B`は`C`に保持されていることを考慮すると、`A`と`B`を
// 2度も書くのは面倒
fn difference<A, B, C>(container: &C) -> i32 where
    C: Contains<A, B> {
    container.last() - container.first()
}

fn main() {
    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!("Does container contain {} and {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("First number: {}", container.first());
    println!("Last number: {}", container.last());

    println!("The difference is: {}", difference(&container));
}

参照

構造体, トレイト

関連型

関連型を使用すると、コンテナ型の中の要素をトレイトの中に 出力型 として書くことで、全体の可読性を上げることができます。トレイトを定義する際の構文は以下のようになります。


#![allow(unused)]
fn main() {
// `A` and `B` are defined in the trait via the `type` keyword.
// (Note: `type` in this context is different from `type` when used for
// aliases).
// `A`と`B`は`type`キーワードを用いてトレイト内で宣言されている。
// (注意: この文脈で使用する`type`は型エイリアスを宣言する際の`type`とは
// 異なることに注意しましょう。)
trait Contains {
    type A;
    type B;

    // Updated syntax to refer to these new types generically.
    // これらの新しい型をジェネリックに使用するために、構文が
    // アップデートされています。
    fn contains(&self, &Self::A, &Self::B) -> bool;
}
}

Containsトレイトを使用する関数において、ABを明示する必要がなくなっていることに注目しましょう。

// Without using associated types
// 関連型を使用しない場合
fn difference<A, B, C>(container: &C) -> i32 where
    C: Contains<A, B> { ... }

// Using associated types
// 使用する場合
fn difference<C: Contains>(container: &C) -> i32 { ... }

前セクションの例を関連型を使用して書きなおしてみましょう。

struct Container(i32, i32);

// A trait which checks if 2 items are stored inside of container.
// Also retrieves first or last value.
// 2つの要素がコンテナ型の中に保持されていることを確認するトレイト。
// また、最初あるいは最後の要素を取り出すこともできる。
trait Contains {
    // Define generic types here which methods will be able to utilize.
    // メソッドが使用できるジェネリック型を定義
    type A;
    type B;

    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

impl Contains for Container {
    // Specify what types `A` and `B` are. If the `input` type
    // is `Container(i32, i32)`, the `output` types are determined
    // as `i32` and `i32`.
    // `A`と`B`がどの型であるかを明示。インプットの型(訳注: つまり`Self`の型)
    // が`Container(i32, i32)`である場合、出力型は`i32`と`i32`となる。
    type A = i32;
    type B = i32;

    // `&Self::A` and `&Self::B` are also valid here.
    // `&i32`の代わりに`&Self::A`または`&self::B`と書いても良い
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    // Grab the first number.
    // 1つ目の数を取得
    fn first(&self) -> i32 { self.0 }

    // Grab the last number.
    // 最後の数を取得
    fn last(&self) -> i32 { self.1 }
}

fn difference<C: Contains>(container: &C) -> i32 {
    container.last() - container.first()
}

fn main() {
    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!("Does container contain {} and {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("First number: {}", container.first());
    println!("Last number: {}", container.last());
    
    println!("The difference is: {}", difference(&container));
}

幽霊型パラメータ

幽霊型(Phantom Type)とは実行時には存在しないけれども、コンパイル時に静的に型チェックされるような型のことです。

構造体などのデータ型は、ジェネリック型パラメータを一つ余分に持ち、それをマーカーとして使ったりコンパイル時の型検査に使ったりすることができます。このマーカーは実際の値を何も持たず、したがって実行時の挙動そのものにはいかなる影響ももたらしません。

以下の例では、そのようなマーカーとして幽霊型(std::marker::PhantomData)を用い、それぞれ異なった型の値を持つタプルを作成します。

use std::marker::PhantomData;

// A phantom tuple struct which is generic over `A` with hidden parameter `B`.
// ジェネリックなタプル構造体。2つ目のパラメータは幽霊型
#[derive(PartialEq)] // Allow equality test for this type.
                     // 比較演算子(`==`)での比較を可能にする。
struct PhantomTuple<A, B>(A,PhantomData<B>);

// A phantom type struct which is generic over `A` with hidden parameter `B`.
// 同様に構造体を定義
#[derive(PartialEq)] // Allow equality test for this type.
                     // 比較演算子での比較を可能にする。
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }

// Note: Storage is allocated for generic type `A`, but not for `B`.
//       Therefore, `B` cannot be used in computations.
// 注意点:  ジェネリック型Aに対してはメモリが割り当てられているが、
//          Bには割り当てられていないため、計算に使うことはできない。

fn main() {
    // Here, `f32` and `f64` are the hidden parameters.
    // PhantomTuple type specified as `<char, f32>`.
    // <char, f32>と型宣言されたPhantomTupleを作成
    let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
    // PhantomTuple type specified as `<char, f64>`.
    // <chr, f64>のPhantomTuple。 PhantomDataがいかなる浮動小数点でもないことに注目
    let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);

    // Type specified as `<char, f32>`.
    // <char, f32>の型が与えられた構造体を作成
    let _struct1: PhantomStruct<char, f32> = PhantomStruct {
        first: 'Q',
        phantom: PhantomData,
    };
    // Type specified as `<char, f64>`.
    // 同様に<char, f64>の構造体
    let _struct2: PhantomStruct<char, f64> = PhantomStruct {
        first: 'Q',
        phantom: PhantomData,
    };
    
    // Compile-time Error! Type mismatch so these cannot be compared:
    // コンパイルエラー!型が違うので`==`で比較することができない!
    //println!("_tuple1 == _tuple2 yields: {}",
    //          _tuple1 == _tuple2);
    
    // Compile-time Error! Type mismatch so these cannot be compared:
    // コンパイルエラー! 型が違うので比較することができない!
    //println!("_struct1 == _struct2 yields: {}",
    //          _struct1 == _struct2);
}

参照

継承(Derive), 構造体, タプル

テストケース: 単位を扱う

共通の単位同士を扱う際のチェックのために、Addを幽霊型を用いた実装にすると便利な場合があります。その場合Addトレイトは以下のようになります。

訳注: RHSはRight Hand Side、つまりAdd(+)演算時の右辺のことです

// This construction would impose: `Self + RHS = Output`
// where RHS defaults to Self if not specified in the implementation.
// このように定義しておくと、`Self + RHS = Output`であることが保証され、
// かつ、impl時にRHSの型が明示されていない場合、デフォルトでSelfと同じに
// なる。
pub trait Add<RHS = Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}

// `Output` must be `T<U>` so that `T<U> + T<U> = T<U>`.
// `Output`は`T<U>`でなくてはならないので`T<U> + T<U> = T<U>`となる。
impl<U> Add for T<U> {
    type Output = T<U>;
    ...
}

以下は全体を示した例です。:

use std::ops::Add;
use std::marker::PhantomData;

/// Create void enumerations to define unit types.
/// 単位を定義するため、空の列挙型を作成。
#[derive(Debug, Clone, Copy)]
enum Inch {}
#[derive(Debug, Clone, Copy)]
enum Mm {}

/// `Length` is a type with phantom type parameter `Unit`,
/// and is not generic over the length type (that is `f64`).
/// `Length`は`Unit`という幽霊型パラメータを持つ型
///
/// `f64` already implements the `Clone` and `Copy` traits.
/// `f64`ははじめから`Clone`、`Copy`トレイトを持っている。
#[derive(Debug, Clone, Copy)]
struct Length<Unit>(f64, PhantomData<Unit>);

/// The `Add` trait defines the behavior of the `+` operator.
/// `Add`トレイトは加算演算子(`+`)の挙動を定義する。
impl<Unit> Add for Length<Unit> {
     type Output = Length<Unit>;

    // add() returns a new `Length` struct containing the sum.
    // add()は`Length`の新しいインスタンスを返す。
    // Lengthの中の値は合計値になっている。
    fn add(self, rhs: Length<Unit>) -> Length<Unit> {
        // `+` calls the `Add` implementation for `f64`.
        // ここでの`+`は`f64`の`Add`実装を呼び出す。
        Length(self.0 + rhs.0, PhantomData)
    }
}

fn main() {
    // Specifies `one_foot` to have phantom type parameter `Inch`.
    // `one_foot`が幽霊型`Inch`を持つように明示する。
    let one_foot:  Length<Inch> = Length(12.0, PhantomData);
    // `one_meter` has phantom type parameter `Mm`.
    // `one_meter`が幽霊型`Mm`を持つように明示する。
    let one_meter: Length<Mm>   = Length(1000.0, PhantomData);

    // `+` calls the `add()` method we implemented for `Length<Unit>`.
    // 以下の`+`は上で定義した`Length<Unit>`用の`add()`メソッドを呼び出す。
    //
    // Since `Length` implements `Copy`, `add()` does not consume
    // `one_foot` and `one_meter` but copies them into `self` and `rhs`.
    // `Length`は`Copy`トレイトを持っているため、`add()`は`one_foot`及び`one_meter`
    // を消費する代わりにそのコピーを作り、`self`、`rhs`として扱う。
    let two_feet = one_foot + one_foot;
    let two_meters = one_meter + one_meter;

    // Addition works.
    // 加算が問題なく実行されていることを確認
    println!("one foot + one_foot = {:?} in", two_feet.0);
    println!("one meter + one_meter = {:?} mm", two_meters.0);

    // Nonsensical operations fail as they should:
    // Compile-time Error: type mismatch.
    // 異なる単位間の加算は意味を成さないので、
    // 以下はきちんとコンパイルエラーになる。
    // コンパイルエラー: タイプミスマッチ
    //let one_feter = one_foot + one_meter;
}

参照

借用(&), トレイトバウンド, 列挙型, impl & self, 演算子のオーバーロード, 参照, トレイト (X for Y), タプル.

スコーピングの規則

所有権、借用、ライフタイムといったRustに特有の概念において、変数のスコープは重要な役割を果たします。すなわち、スコープの存在によってコンパイラは借用は可能か否か、メモリ上の資源は解放可能か、変数はいつ作成され、いつ破棄されるか。といったことを知るのです。

RAII

Rustの変数は単にデータをスタック上に保持するだけのものではありません。例えばヒープメモリを確保するBox<T>のように、変数はメモリ上の資源を 保有 する場合もあるのです。RustはRAII(Resource Acquisition Is Initialization)を強制するので、オブジェクトがスコープを抜けると、必ずデストラクタが呼び出されてそのオブジェクトが保持していた資源が解放されます。

この振る舞いは リソースリーク (resource leak)バグを防ぐのに役立ちます。手動でメモリを解放したり、メモリリークバグにわずらわされたりすることはなくなるのです!簡単な例で説明しましょう。

// raii.rs
fn create_box() {
    // Allocate an integer on the heap
    // 整数をヒープ上に確保
    let _box1 = Box::new(3i32);

    // `_box1` is destroyed here, and memory gets freed
    // `_box1`はここで破棄され、メモリは解放される。
}

fn main() {
    // Allocate an integer on the heap
    // 整数をヒープ上に確保
    let _box2 = Box::new(5i32);

    // A nested scope:
    // ネストしたスコープ
    {
        // Allocate an integer on the heap
        // 整数をヒープ上に確保
        let _box3 = Box::new(4i32);

        // `_box3` is destroyed here, and memory gets freed
        // `_box3`はここで破棄され、メモリは解放される。
    }

    // Creating lots of boxes just for fun
    // There's no need to manually free memory!
    // お遊びで大量のボックスを作る。
    // もちろん手動で開放する必要はないよ!
    for _ in 0u32..1_000 {
        create_box();
    }

    // `_box2` is destroyed here, and memory gets freed
    // `_box2`はここで破棄され、メモリは解放される。
}

valgrindを用いて、メモリエラーが起きていないか2重チェックすることももちろん可能です。

$ rustc raii.rs && valgrind ./raii
==26873== Memcheck, a memory error detector
==26873== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26873== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==26873== Command: ./raii
==26873==
==26873==
==26873== HEAP SUMMARY:
==26873==     in use at exit: 0 bytes in 0 blocks
==26873==   total heap usage: 1,013 allocs, 1,013 frees, 8,696 bytes allocated
==26873==
==26873== All heap blocks were freed -- no leaks are possible
==26873==
==26873== For counts of detected and suppressed errors, rerun with: -v
==26873== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

リークはないみたいですね!

デストラクタ

Rustにおけるデストラクタの概念はDropトレイトによって提供されています。デストラクタは資源がスコープを抜けるときに呼び出されます。Dropトレイトは型定義のたびに必ず実装する必要があるわけではなく、デストラクタに独自のロジックが必要な場合にのみ実装します。

下のコードを実行して、Dropトレイトの動作を確認してみましょう。main関数内の変数がスコープを抜けるときにカスタムデストラクタが呼び出されるはずです。

struct ToDrop;

impl Drop for ToDrop {
    fn drop(&mut self) {
        println!("ToDrop is being dropped");
    }
}

fn main() {
    let x = ToDrop;
    println!("Made a ToDrop!");
}

参照

ボックス

所有権とムーブ

変数には自身の保持する資源を開放する責任があるため、 資源は一度に一つの所有者 しか持つことができません。これはまた、資源を2度以上開放することができないということでもあります。ここで、全ての変数が資源を所有するわけではないということに注意しましょう。(e.g. 参照

変数をアサインする(let x = y)際や、関数に引数を値渡しする(foo(x))際は、資源の 所有権(ownership) が移動します。Rustっぽく言うと、「 ムーブ 」です。

資源を移動すると、それまでの所有者(訳注: 変数などのこと)を使用することはできなくなります。これによりダングリングポインタの発生を防げます。

// This function takes ownership of the heap allocated memory
// この関数はヒープメモリ上の資源の所有権を取る。
fn destroy_box(c: Box<i32>) {
    println!("Destroying a box that contains {}", c);

    // `c` is destroyed and the memory freed
    // `c`は破棄されメモリは開放される。
}

fn main() {
    // _Stack_ allocated integer
    // _スタック_上に置かれた整数
    let x = 5u32;

    // *Copy* `x` into `y` - no resources are moved
    // `x`を`y`に *コピー* する。元の値が移動するわけではない。
    let y = x;

    // Both values can be independently used
    // 両方の値はそれぞれ独立に使うことができる。
    println!("x is {}, and y is {}", x, y);

    // `a` is a pointer to a _heap_ allocated integer
    // `a`は_ヒープ_上の整数へのポインタ
    let a = Box::new(5i32);

    println!("a contains: {}", a);

    // *Move* `a` into `b`
    // `a`を`b`に *ムーブ* する。
    let b = a;
    // The pointer address of `a` is copied (not the data) into `b`.
    // Both are now pointers to the same heap allocated data, but
    // `b` now owns it.
    // すなわち、`a`の指すメモリ上の番地が`b`にコピーされるため
    // いずれもヒープ上の同じ値を指すポインタとなる。しかし所有権は`b`にある。
    
    // Error! `a` can no longer access the data, because it no longer owns the
    // heap memory
    // エラー! `a`は所有権を持たないため、ヒープ上のデータにアクセスできない。
    //println!("a contains: {}", a);
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにここをアンコメントしてみましょう。

    // This function takes ownership of the heap allocated memory from `b`
    // この関数はヒープメモリ上の所有権を`b`から取る。
    destroy_box(b);

    // Since the heap memory has been freed at this point, this action would
    // result in dereferencing freed memory, but it's forbidden by the compiler
    // Error! Same reason as the previous Error
    // この時点でヒープメモリ上の資源は開放されているので、次の操作は
    // 解放済みメモリをデリファレンスすることになる。しかしそれはコンパイラが許さない。
    // エラー! 上述の理由より
    //println!("b contains: {}", b);
    // TODO ^ Try uncommenting this line
    // TODO ^ 試しにここをアンコメントしてみましょう。
}

ミュータビリティ

データのミュータビリティは所有権を移譲した際に変更できます。

fn main() {
    let immutable_box = Box::new(5u32);

    println!("immutable_box contains {}", immutable_box);

    // Mutability error
    // ミュータビリティエラー
    //*immutable_box = 4;

    // *Move* the box, changing the ownership (and mutability)
    // boxを *ムーブ* する、同時に所有権とミュータビリティを変更する。
    let mut mutable_box = immutable_box;

    println!("mutable_box contains {}", mutable_box);

    // Modify the contents of the box
    // boxの内容を変更
    *mutable_box = 4;

    println!("mutable_box now contains {}", mutable_box);
}

Partial moves

Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used.

fn main() {
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }

    let person = Person {
        name: String::from("Alice"),
        age: 20,
    };

    // `name` is moved out of person, but `age` is referenced
    let Person { name, ref age } = person;

    println!("The person's age is {}", age);

    println!("The person's name is {}", name);

    // Error! borrow of partially moved value: `person` partial move occurs
    //println!("The person struct is {:?}", person);

    // `person` cannot be used but `person.age` can be used as it is not moved
    println!("The person's age from person struct is {}", person.age);
}

See also:

destructuring

借用

実際には、データの所有権を完全に受け渡すことなく一時的にアクセスしたいという場合がほとんどです。そのために、Rustでは 借用(borrowing) という仕組みを用います。値そのもの(T)を受け渡すのではなく、そのリファレンス(&T)を渡すのです。

コンパイラは借用チェッカを用いてリファレンスが 常に 有効なオブジェクトへの参照であることを、コンパイル時に保証します。つまり、あるオブジェクトへのリファレンスが存在しているならば、そのオブジェクトを破壊することはできないということです。

// This function takes ownership of a box and destroys it
// この関数はボックスの所有権を奪い、破棄する。
fn eat_box_i32(boxed_i32: Box<i32>) {
    println!("Destroying box that contains {}", boxed_i32);
}

// This function borrows an i32
// この関数はi32を借用する
fn borrow_i32(borrowed_i32: &i32) {
    println!("This int is: {}", borrowed_i32);
}

fn main() {
    // Create a boxed i32, and a stacked i32
    // ボックス化された整数を作成
    let boxed_i32 = Box::new(5_i32);
    let stacked_i32 = 6_i32;

    // Borrow the contents of the box. Ownership is not taken,
    // so the contents can be borrowed again.
    // Boxの中身を借用。所有権を奪うわけではないため、
    // 直後にもう一度借用できる。
    borrow_i32(&boxed_i32);
    borrow_i32(&stacked_i32);

    {
        // Take a reference to the data contained inside the box
        // ボックス内の要素に対する参照を取得
        let _ref_to_i32: &i32 = &boxed_i32;

        // Error!
        // Can't destroy `boxed_i32` while the inner value is borrowed later in scope.
        // エラー!
        // ボックス内の要素が借用されているため、`boxed_i32`を破棄する
        // ことはできない。
        eat_box_i32(boxed_i32);
        // FIXME ^ Comment out this line
        // FIXME ^ この行をコメントアウトしましょう。

        // Attempt to borrow `_ref_to_i32` after inner value is destroyed
        borrow_i32(_ref_to_i32);
        // `_ref_to_i32` goes out of scope and is no longer borrowed.
        // ここで`_ref_to_i32`はスコープを抜け、借用もなくなります。
    }

    // `boxed_i32` can now give up ownership to `eat_box` and be destroyed
    // ここでようやく、`eat_box`は所有権を移譲し、破棄することができます。
    eat_box_i32(boxed_i32);
}

ミュータビリティ

ミュータブルなデータは&mut Tでミュータブルに(変更可能な形で)借用することができます。これは ミュータブルな参照 と呼ばれ、読み込み・書き込みの権限を借用者に与えます。対照的に&Tはデータをイミュータブルな参照を介して借用し、借用した側はデータを読み込みはできますが書き込みはできません。

#[allow(dead_code)]
#[derive(Clone, Copy)]
struct Book {
    // `&'static str` is a reference to a string allocated in read only memory
    // `&'static str`はread-onlyメモリ上の文字列への参照
    author: &'static str,
    title: &'static str,
    year: u32,
}

// This function takes a reference to a book
// この関数はBook型へのリファレンスを取る。
fn borrow_book(book: &Book) {
    println!("I immutably borrowed {} - {} edition", book.title, book.year);
}

// This function takes a reference to a mutable book and changes `year` to 2014
// この関数はミュータブルなBook型へのミュータブルなリファレンスを取り、
// `year`を2014へ変更する。
fn new_edition(book: &mut Book) {
    book.year = 2014;
    println!("I mutably borrowed {} - {} edition", book.title, book.year);
}

fn main() {
    // Create an immutable Book named `immutabook`
    // `immutabook`という名のイミュータブルなBookを作成
    let immutabook = Book {
        // string literals have type `&'static str`
        // 「"」で囲まれた部分は`&'static str`型になる。
        author: "Douglas Hofstadter",
        title: "Gödel, Escher, Bach",
        year: 1979,
    };

    // Create a mutable copy of `immutabook` and call it `mutabook`
    // `immutabook`のミュータブルなコピーを作成し、`mutabook`と名付ける
    let mut mutabook = immutabook;
    
    // Immutably borrow an immutable object
    // イミュータブルなオブジェクトをイミュータブルに借用する
    borrow_book(&immutabook);

    // Immutably borrow a mutable object
    // ミュータブルなオブジェクトをイミュータブルに借用する
    borrow_book(&mutabook);
    
    // Borrow a mutable object as mutable
    // ミュータブルなオブジェクトをミュータブルに借用する
    new_edition(&mut mutabook);
    
    // Error! Cannot borrow an immutable object as mutable
    // エラー!イミュータブルなオブジェクトをミュータブルに借用することはできない
    new_edition(&mut immutabook);
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう
}

参照

static

エイリアス

データは一度にいくつでもイミュータブルに借用することができますが、その間オリジナルのデータをミュータブルに借用することはできません。一方でミュータブルな借用は一度に 一つ しか借用することができません。オリジナルのデータをもう一度借用できるのはミュータブルな参照が最後に使われた場所より あとで なければいけません。

struct Point { x: i32, y: i32, z: i32 }

fn main() {
    let mut point = Point { x: 0, y: 0, z: 0 };

    let borrowed_point = &point;
    let another_borrow = &point;

    // Data can be accessed via the references and the original owner
    // データは元々の持ち主と参照の両方からアクセスすることができます。
    println!("Point has coordinates: ({}, {}, {})",
                borrowed_point.x, another_borrow.y, point.z);

    // Error! Can't borrow `point` as mutable because it's currently
    // borrowed as immutable.
    // エラー!pointはすでにイミュータブルに借用されているため、
    // ミュータブルに借用することができない。
    // let mutable_borrow = &mut point;
    // TODO ^ Try uncommenting this line

    // The borrowed values are used again here
    println!("Point has coordinates: ({}, {}, {})",
                borrowed_point.x, another_borrow.y, point.z);

    // The immutable references are no longer used for the rest of the code so
    // it is possible to reborrow with a mutable reference.
    let mutable_borrow = &mut point;

    // Change data via mutable reference
    // ミュータブルなリファレンスを介してデータを変更する
    mutable_borrow.x = 5;
    mutable_borrow.y = 2;
    mutable_borrow.z = 1;

    // Error! Can't borrow `point` as immutable because it's currently
    // borrowed as mutable.
    // エラー!`point`はすでにミュータブルに借用されているため、
    // イミュータブルに借用することはできない。
    // let y = &point.y;
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。

    // Error! Can't print because `println!` takes an immutable reference.
    // エラー!`println!`はイミュータブルなリファレンスを取るため、printできません。
    // println!("Point Z coordinate is {}", point.z);
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。

    // Ok! Mutable references can be passed as immutable to `println!`
    println!("Point has coordinates: ({}, {}, {})",
                mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);

    // The mutable reference is no longer used for the rest of the code so it
    // is possible to reborrow
    let new_borrowed_point = &point;
    println!("Point now has coordinates: ({}, {}, {})",
             new_borrowed_point.x, new_borrowed_point.y, new_borrowed_point.z);
}

refパターン

letを介してデストラクトやパターンマッチングを行う場合、refキーワードを用いて構造体・タプルのフィールドへのリファレンスを取得することができます。以下の例ではこれが有用になる例を幾つかお見せします。

#[derive(Clone, Copy)]
struct Point { x: i32, y: i32 }

fn main() {
    let c = 'Q';

    // A `ref` borrow on the left side of an assignment is equivalent to
    // an `&` borrow on the right side.
    // 左辺に`ref`をつけることによる借用と、右辺に`&`をつけることによる借用は等価
    let ref ref_c1 = c;
    let ref_c2 = &c;

    println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2);

    let point = Point { x: 0, y: 0 };

    // `ref` is also valid when destructuring a struct.
    // `ref`は構造体をデストラクトする際にも有用
    let _copy_of_x = {
        // `ref_to_x` is a reference to the `x` field of `point`.
        // `ref_to_x`は`point`の`x`フィールドへの参照
        let Point { x: ref ref_to_x, y: _ } = point;

        // Return a copy of the `x` field of `point`.
        // `point`の`x`フィールドへのコピーを返す。
        *ref_to_x
    };

    // A mutable copy of `point`
    // `point`へのミュータブルなコピー
    let mut mutable_point = point;

    {
        // `ref` can be paired with `mut` to take mutable references.
        // `ref`は`mut`とともに使い、ミュータブルな参照を取ることもできる。
        let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point;

        // Mutate the `y` field of `mutable_point` via a mutable reference.
        // `mutable_point`の`y`というミュータブルなフィールドの値を変更する。
        *mut_ref_to_y = 1;
    }

    println!("point is ({}, {})", point.x, point.y);
    println!("mutable_point is ({}, {})", mutable_point.x, mutable_point.y);

    // A mutable tuple that includes a pointer
    // ポインタを含むミュータブルなタプル
    let mut mutable_tuple = (Box::new(5u32), 3u32);
    
    {
        // Destructure `mutable_tuple` to change the value of `last`.
        // `mutable_tuple`をデストラクトして、`last`の値を変更
        let (_, ref mut last) = mutable_tuple;
        *last = 2u32;
    }
    
    println!("tuple is {:?}", mutable_tuple);
}

ライフタイム

ライフタイム はコンパイラ(借用チェッカーと呼ばれる場合もあります)が、全ての借用に問題がないことを確認するために使用する仕組みです。正確にいうと、変数のライフタイムは作成時に開始し、破棄された時に終了します。ライフタイムとスコープは同時に語られることが多いですが、同じものではありません。

例として&を用いて変数を借用する場合をあげましょう。借用のライフタイムは宣言時に決定し、そこから貸し手が破棄されるまで続きます。しかしながら、借用のスコープは参照が使われる際に決定します。

以下の例からこのセクションの残りでは、ライフタイムとスコープの関係、そしてそれらがいかに異なるものであるかを見ていきます。

// Lifetimes are annotated below with lines denoting the creation
// and destruction of each variable.
// `i` has the longest lifetime because its scope entirely encloses 
// both `borrow1` and `borrow2`. The duration of `borrow1` compared 
// to `borrow2` is irrelevant since they are disjoint.
// 以下では、変数の作成から破棄までのライフタイムを線で示しています。
// `i`は最長のライフタイムを持ち、そのスコープは`borrow1`および`borrow2`
// のスコープを完全に包含します。`borrow1`と`borrow2`の存続期間は一切重なりません。
fn main() {
    let i = 3; // Lifetime for `i` starts. ────────────────┐
    //                                                     │
    { //                                                   │
        let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
        //                                                ││
        println!("borrow1: {}", borrow1); //              ││
    } // `borrow1 ends. ──────────────────────────────────┘│
    //                                                     │
    //                                                     │
    { //                                                   │
        let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
        //                                                ││
        println!("borrow2: {}", borrow2); //              ││
    } // `borrow2` ends. ─────────────────────────────────┘│
    //                                                     │
}   // Lifetime ends. ─────────────────────────────────────┘

Note that no names or types are assigned to label lifetimes. This restricts how lifetimes will be able to be used as we will see.

明示的アノテーション

借用チェッカーは参照がどれだけの間有効かを決定するために、明示的なアノテーションを使用します。ライフタイムが省略1されなかった場合、Rustは参照のライフタイムがどのようなものであるか、明示的なアノテーションを必要とします。

foo<'a>
// `foo` has a lifetime parameter `'a`
// `foo`は`'a`というライフタイムパラメータを持ちます。

クロージャと同様、ライフタイムの使用はジェネリクスを必要とします。もう少し詳しく言うと、この書き方は「fooのライフタイムは'aのそれを超えることはない。」ということを示しており、型を明示した場合'a&'a Tとなるということです。

ライフタイムが複数ある場合も、同じような構文になります。

foo<'a, 'b>
// `foo` has lifetime parameters `'a` and `'b`
// `foo`は`'a`と`'b`というライフタイムパラメータを持ちます。

この場合は、fooのライフタイムは'a'bいずれよりも 長くなってはなりません。

以下はライフタイムを明示的に書く場合の例です。

// `print_refs` takes two references to `i32` which have different
// lifetimes `'a` and `'b`. These two lifetimes must both be at
// least as long as the function `print_refs`.
// `print_refs`は`i32`への参照を2つとり、それぞれ`'a`と`'b`という
// ライフタイムを持つ。これらのライフタイムは最短でも`print_refs`
// 関数と同じになる。
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
    println!("x is {} and y is {}", x, y);
}

// A function which takes no arguments, but has a lifetime parameter `'a`.
// 引数を取らないがライフタイムパラメータ`'a`を持つ関数。
fn failed_borrow<'a>() {
    let _x = 12;

    // ERROR: `_x` does not live long enough
    // エラー: `_x`の寿命が短すぎる。
    let y: &'a i32 = &_x;
    // Attempting to use the lifetime `'a` as an explicit type annotation 
    // inside the function will fail because the lifetime of `&_x` is shorter
    // than that of `y`. A short lifetime cannot be coerced into a longer one.
    // `&_x`のライフタイムは`y`のそれよりも短いため、関数内で`'a`を使用して
    // 変数のライフタイムを指定しようとすると失敗する。つまり、短いライフタイム
    // を持つ参照をより長いものに強制的に代入することはできない。
}

fn main() {
    // Create variables to be borrowed below.
    // 下で借用するための変数を作成
    let (four, nine) = (4, 9);
    
    // Borrows (`&`) of both variables are passed into the function.
    // 2つの変数の借用(`&`)が関数に渡される。
    print_refs(&four, &nine);
    // Any input which is borrowed must outlive the borrower. 
    // In other words, the lifetime of `four` and `nine` must 
    // be longer than that of `print_refs`.
    // 借用された変数の寿命は、借り手のそれよりも長くなくてはならない。
    // つまり、`four`、`nine`のライフタイムは`print_refs`のそれよりも
    // 長くなくてはならない。
    
    failed_borrow();
    // `failed_borrow` contains no references to force `'a` to be 
    // longer than the lifetime of the function, but `'a` is longer.
    // Because the lifetime is never constrained, it defaults to `'static`.
    // `failed_borrow`は関数のライフタイムよりも`'a`を長くさせるような
    // 参照を持たないが、それでも`'a`のほうが長くなる。なぜならそのような
    // 場合`'a`はデフォルトで`'static`になるからである。
}
1

省略 はライフタイムが暗黙のうちに(プログラマから見えない形で)アノテートされることを指します。

参照

ジェネリクス, クロージャ

関数

省略をしない場合、ライフタイムのシグネチャ(e.g. <'a>)を持つ関数にはいくつかの制限があります。

  • 全ての変数においてライフタイムを明示しなくてはならない。
  • 返り値となる参照はすべて引数と同じライフタイムか、staticライフタイムを持たなくてはならない

加えて、引数のない関数から参照を返す場合、それが結果的に無効なデータへの参照になるならば、禁止されている

// One input reference with lifetime `'a` which must live
// at least as long as the function.
// 引数として`'a`のライフタイムで参照を一つ取る。最低でもこの関数
// と同じだけの長さでなくてはならない。
fn print_one<'a>(x: &'a i32) {
    println!("`print_one`: x is {}", x);
}

// Mutable references are possible with lifetimes as well.
// ミュータブルな参照でも同様
fn add_one<'a>(x: &'a mut i32) {
    *x += 1;
}

// Multiple elements with different lifetimes. In this case, it
// would be fine for both to have the same lifetime `'a`, but
// in more complex cases, different lifetimes may be required.
// 異なるライフタイムを持つ複数の引数がある場合。
// ここでは1種類のライフタイムでも問題はないが、より複雑なケースでは
// 異なるライフタイムが必要になる場合がある。
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
    println!("`print_multi`: x is {}, y is {}", x, y);
}

// Returning references that have been passed in is acceptable.
// However, the correct lifetime must be returned.
// 受け取った参照をそのまま返すことに問題はないが、適切なライフタイム
// でなくてはならない。
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x }

//fn invalid_output<'a>() -> &'a String { &String::from("foo") }
// The above is invalid: `'a` must live longer than the function.
// Here, `&String::from("foo")` would create a `String`, followed by a
// reference. Then the data is dropped upon exiting the scope, leaving
// a reference to invalid data to be returned.
// `'a`は関数より長くなくてはならないため上の関数は正しくない。
// ここでは、`&String::from("foo")`は`String`のデータとそれへの参照を作り出す。
// その後データはスコープを抜けるとともに破棄される。そのため、
// 不適切なデータに対する参照を返すことになってしまう。

fn main() {
    let x = 7;
    let y = 9;
    
    print_one(&x);
    print_multi(&x, &y);
    
    let z = pass_x(&x, &y);
    print_one(z);

    let mut t = 3;
    add_one(&mut t);
    print_one(&t);
}

参照

functions

メソッド

メソッドのライフタイムは関数に似ている。

struct Owner(i32);

impl Owner {
    // Annotate lifetimes as in a standalone function.
    // 通常の関数と同様にライフタイムを明示
    fn add_one<'a>(&'a mut self) { self.0 += 1; }
    fn print<'a>(&'a self) {
        println!("`print`: {}", self.0);
    }
}

fn main() {
    let mut owner = Owner(18);

    owner.add_one();
    owner.print();
}

参照

メソッド

構造体

構造体におけるライフタイムも関数のそれと似ている。

// A type `Borrowed` which houses a reference to an
// `i32`. The reference to `i32` must outlive `Borrowed`.
// `i32`への参照をメンバに持つ`Borrowed`型。
// 参照は`Borrowed`自体よりも長生きでなくてはならない。
#[derive(Debug)]
struct Borrowed<'a>(&'a i32);

// Similarly, both references here must outlive this structure.
// 同様に、ここでも参照は構造体よりも長生きでなくてはならない。
#[derive(Debug)]
struct NamedBorrowed<'a> {
    x: &'a i32,
    y: &'a i32,
}

// An enum which is either an `i32` or a reference to one.
// `i32`、あるいは`i32`への参照のいずれかとなる列挙型
#[derive(Debug)]
enum Either<'a> {
    Num(i32),
    Ref(&'a i32),
}

fn main() {
    let x = 18;
    let y = 15;

    let single = Borrowed(&x);
    let double = NamedBorrowed { x: &x, y: &y };
    let reference = Either::Ref(&x);
    let number    = Either::Num(y);

    println!("x is borrowed in {:?}", single);
    println!("x and y are borrowed in {:?}", double);
    println!("x is borrowed in {:?}", reference);
    println!("y is *not* borrowed in {:?}", number);
}

参照

structs

Traits

Annotation of lifetimes in trait methods basically are similar to functions. Note that impl may have annotation of lifetimes too.

// A struct with annotation of lifetimes.
#[derive(Debug)]
struct Borrowed<'a> {
    x: &'a i32,
}

// Annotate lifetimes to impl.
impl<'a> Default for Borrowed<'a> {
    fn default() -> Self {
        Self {
            x: &10,
        }
    }
}

fn main() {
    let b: Borrowed = Default::default();
    println!("b is {:?}", b);
}

参照

traits

ライフタイム境界

ジェネリック型に境界(bound)を与え、特定のトレイトを実装していることを保証できるのと同様、ライフタイム(それ自身ジェネリック型)にも境界を与えることができます。:は、ここでは多少異なる意味を持ちますが+は同じです。以下の構文の意味をチェックしてください。

  1. T: 'a: T内の 全ての 参照は'aよりも長生きでなくてはならない
  2. T: Trait + 'a: 上に加えてTTraitという名のトレイトを実装してなくてはならない。

上記の構文を実際に動く例で見ていきましょう。whereキーワードの後に注目してください。

use std::fmt::Debug; // Trait to bound with.
                     // ライフタイムを紐付けるトレイト

#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// `Ref` contains a reference to a generic type `T` that has
// an unknown lifetime `'a`. `T` is bounded such that any
// *references* in `T` must outlive `'a`. Additionally, the lifetime
// of `Ref` may not exceed `'a`.
// `Ref`は`'a`というライフタイムを持つジェネリック型`T`に対する参照を持ち、
// `T`の値に対する *参照* は必ず`'a`よりも長生きでなくてはならない。
// さらに、`Ref`のライフタイムは`'a`を超えてはならない。

// A generic function which prints using the `Debug` trait.
// `Debug`トレイトを利用してプリントを行うジェネリック関数
fn print<T>(t: T) where
    T: Debug {
    println!("`print`: t is {:?}", t);
}

// Here a reference to `T` is taken where `T` implements
// `Debug` and all *references* in `T` outlive `'a`. In
// addition, `'a` must outlive the function.
// `Debug`を実装している`T`への参照を取る。`T`への *参照* は
// 必ず`'a`よりも長生きでなくてはならない。さらに、`'a`は
// 関数自体よりも長生きでなくてはならない。
fn print_ref<'a, T>(t: &'a T) where
    T: Debug + 'a {
    println!("`print_ref`: t is {:?}", t);
}

fn main() {
    let x = 7;
    let ref_x = Ref(&x);

    print_ref(&ref_x);
    print(ref_x);
}

参照

ジェネリクス, ジェネリック境界, 境界が複数の場合

圧縮

長いライフタイムは、短いものに圧縮(coerce)することで、そのままでは動作しないスコープの中でも使用できるようになります。これは、Rustコンパイラが推論の結果として圧縮する場合と、複数のライフタイムを比較して圧縮する場合があります。

// Here, Rust infers a lifetime that is as short as possible.
// The two references are then coerced to that lifetime.
// ここではRustはライフタイムを出来る限り短く見積もり、
// 2つの参照をそのライフタイムに押し込める。
fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 {
    first * second
}

// `<'a: 'b, 'b>` reads as lifetime `'a` is at least as long as `'b`.
// Here, we take in an `&'a i32` and return a `&'b i32` as a result of coercion.
// `<'a: 'b, 'b>`は「ライフタイム`'a`は最低でも`'b`と同じ長さ」と読める。
// ここでは、`&'a i32`をとり、`&'b i32`に圧縮して返す。
fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
    first
}

fn main() {
    let first = 2; // Longer lifetime
                   // 長いライフタイム
    
    {
        let second = 3; // Shorter lifetime
                        // 短いライフタイム
        
        println!("The product is {}", multiply(&first, &second));
        println!("{} is the first", choose_first(&first, &second));
    };
}

スタティックライフタイム

Rustにはいくつかの予約されたライフタイム名があります。その1つがstaticで、2つの状況で使用することがあります。

// A reference with 'static lifetime:
let s: &'static str = "hello world";

// 'static as part of a trait bound:
fn generic<T>(x: T) where T: 'static {}

Both are related but subtly different and this is a common source for confusion when learning Rust. Here are some examples for each situation:

Reference lifetime

As a reference lifetime 'static indicates that the data pointed to by the reference lives for the entire lifetime of the running program. It can still be coerced to a shorter lifetime.

There are two ways to make a variable with 'static lifetime, and both are stored in the read-only memory of the binary:

  • static宣言とともに定数を作成する。
  • 文字列リテラル で&'static str型を持つ変数を作成する。

では、それぞれの方法の例を見ていきましょう。

// Make a constant with `'static` lifetime.
// `'static`ライフタイムを持つ定数を作成
static NUM: i32 = 18;

// Returns a reference to `NUM` where its `'static`
// lifetime is coerced to that of the input argument.
// `NUM`への参照を返す。ライフタイムは`'static`から引数の
// ライフタイムへと圧縮されている。
fn coerce_static<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn main() {
    {
        // Make a `string` literal and print it:
        // 文字列リテラルを用いて変数を作成し、プリントする
        let static_string = "I'm in read-only memory";
        println!("static_string: {}", static_string);

        // When `static_string` goes out of scope, the reference
        // can no longer be used, but the data remains in the binary.
        // `static_string`がスコープから抜けると、参照は使用することが
        // できなくなるが、データはバイナリ中に残る。
    }

    {
        // Make an integer to use for `coerce_static`:
        // `coerce_static`関数を呼び出すために、整数を作成
        let lifetime_num = 9;

        // Coerce `NUM` to lifetime of `lifetime_num`:
        // `NUM`を`lifetime_num`のライフタイムへと圧縮
        let coerced_static = coerce_static(&lifetime_num);

        println!("coerced_static: {}", coerced_static);
    }

    println!("NUM: {} stays accessible!", NUM);
}

Trait bound

As a trait bound, it means the type does not contain any non-static references. Eg. the receiver can hold on to the type for as long as they want and it will never become invalid until they drop it.

It's important to understand this means that any owned data always passes a 'static lifetime bound, but a reference to that owned data generally does not:

use std::fmt::Debug;

fn print_it( input: impl Debug + 'static ) {
    println!( "'static value passed in is: {:?}", input );
}

fn main() {
    // i is owned and contains no references, thus it's 'static:
    let i = 5;
    print_it(i);

    // oops, &i only has the lifetime defined by the scope of
    // main(), so it's not 'static:
    print_it(&i);
}

The compiler will tell you:

error[E0597]: `i` does not live long enough
  --> src/lib.rs:15:15
   |
15 |     print_it(&i);
   |     ---------^^--
   |     |         |
   |     |         borrowed value does not live long enough
   |     argument requires that `i` is borrowed for `'static`
16 | }
   | - `i` dropped here while still borrowed

参照

'static 定数

省略

ライフタイムのパターンのうちのいくつかは、他と比べてあまりにも一般的に使用されるため、タイプ量を減らし可読性を上げるために省くことができます。これは省略として知られており、それらのパターンが一般的であるというだけの理由で存在しています。

以下のコードでは省略の例を幾つかお見せします。より完全な説明を見たい場合は、「プログラミング言語Rust」のライフタイムの省略の項を見てください。

// `elided_input` and `annotated_input` essentially have identical signatures
// because the lifetime of `elided_input` is inferred by the compiler:
// `elided_input`のライフタイムはコンパイラによって自動的に付与されるため
// 以下の2つは同一のライフタイムシグネチャを持つ。
fn elided_input(x: &i32) {
    println!("`elided_input`: {}", x);
}

fn annotated_input<'a>(x: &'a i32) {
    println!("`annotated_input`: {}", x);
}

// Similarly, `elided_pass` and `annotated_pass` have identical signatures
// because the lifetime is added implicitly to `elided_pass`:
// 同様に、以下の2つの関数も全く同じライフタイムシグネチャを持つ。
fn elided_pass(x: &i32) -> &i32 { x }

fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x }

fn main() {
    let x = 3;

    elided_input(&x);
    annotated_input(&x);

    println!("`elided_pass`: {}", elided_pass(&x));
    println!("`annotated_pass`: {}", annotated_pass(&x));
}

参照

ライフタイムの省略

トレイト

トレイト(trait)とは任意の型となりうるSelfに対して定義されたメソッドの集合のことです。同じトレイト内で宣言されたメソッド同士はお互いにアクセスすることができます。

トレイトはあらゆるデータ型に実装することができます。以下の例ではまずAnimalというメソッドの集合を定義し、その後AnimalトレイトをSheepというデータ型に対して実装します。これによりAnimalのメソッドをSheepが使用することが可能になります。

struct Sheep { naked: bool, name: &'static str }

trait Animal {
    // Associated function signature; `Self` refers to the implementor type.
    // 関連関数のシグネチャ。
    // `Self` はこのトレイトを実装している型になる。
    fn new(name: &'static str) -> Self;

    // Method signatures; these will return a string.
    // メソッドのシグネチャ。
    // これらの関数は文字列を返す。
    fn name(&self) -> &'static str;
    fn noise(&self) -> &'static str;

    // Traits can provide default method definitions.
    // メソッドのデフォルトの挙動を定義することもできる。
    fn talk(&self) {
        println!("{} says {}", self.name(), self.noise());
    }
}

impl Sheep {
    fn is_naked(&self) -> bool {
        self.naked
    }

    fn shear(&mut self) {
        if self.is_naked() {
            // Implementor methods can use the implementor's trait methods.
            // メソッドをある型に実装する際に、その型のトレイトメソッドを
            // 使用することができる。
            println!("{} is already naked...", self.name());
        } else {
            println!("{} gets a haircut!", self.name);

            self.naked = true;
        }
    }
}

// Implement the `Animal` trait for `Sheep`.
// `Animal`というトレイトを`Sheep`に実装する。
impl Animal for Sheep {
    // `Self` is the implementor type: `Sheep`.
    // `Self`は実装対象の型: ここでは`Sheep`
    fn new(name: &'static str) -> Sheep {
        Sheep { name: name, naked: false }
    }

    fn name(&self) -> &'static str {
        self.name
    }

    fn noise(&self) -> &'static str {
        if self.is_naked() {
            "baaaaah?"
        } else {
            "baaaaah!"
        }
    }
    
    // Default trait methods can be overridden.
    // デフォルトのトレイトメソッドはオーバーライドすることができる。
    fn talk(&self) {
        // For example, we can add some quiet contemplation.
        // 例えば、静かに熟考させてみる。
        println!("{} pauses briefly... {}", self.name, self.noise());
    }
}

fn main() {
    // Type annotation is necessary in this case.
    // この場合、型アノテーションが必須。
    let mut dolly: Sheep = Animal::new("Dolly");
    // TODO ^ Try removing the type annotations.
    // TODO ^ ここの型アノテーションを消してみましょう。

    dolly.talk();
    dolly.shear();
    dolly.talk();
}

継承(Derive)

コンパイラには、[#derive]アトリビュートを用いることで型に対して特定のトレイトの標準的な実装を提供する機能があります。より複雑なことを行わせたい場合には、同名のトレイトを手動で実装することも可能です。

以下はderive可能なトレイトの一覧です。

  • 型の比較に関連するトレイト: Eq, PartialEq, Ord, PartialOrd
  • Clone, これはコピーによって&TからTを作成するトレイト
  • Copy, to give a type 'copy semantics' instead of 'move semantics'.
  • Hash, これは&Tからハッシュ値を計算するためのトレイト
  • Default, これは空っぽのインスタンスを作成するためのトレイト
  • Debug, これは{:?}フォーマッタを利用して値をフォーマットするためのトレイト
// `Centimeters`, a tuple struct that can be compared
// `Centimeters`は比較可能なタプルになる
#[derive(PartialEq, PartialOrd)]
struct Centimeters(f64);

// `Inches`, a tuple struct that can be printed
// `Inches`はプリント可能なタプルになる
#[derive(Debug)]
struct Inches(i32);

impl Inches {
    fn to_centimeters(&self) -> Centimeters {
        let &Inches(inches) = self;

        Centimeters(inches as f64 * 2.54)
    }
}

// `Seconds`, a tuple struct with no additional attributes
// `Seconds`には特にアトリビュートを付け加えない。
struct Seconds(i32);

fn main() {
    let _one_second = Seconds(1);

    // Error: `Seconds` can't be printed; it doesn't implement the `Debug` trait
    // エラー: `Seconds`はプリントできない。これは`Debug`トレイトを実装していないため
    //println!("One second looks like: {:?}", _one_second);
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。

    // Error: `Seconds` can't be compared; it doesn't implement the `PartialEq` trait
    // エラー: `Seconds`は比較できない。これは`PartialEq`トレイトを実装していないため
    //let _this_is_true = (_one_second == _one_second);
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう

    let foot = Inches(12);

    println!("One foot equals {:?}", foot);

    let meter = Centimeters(100.0);

    let cmp =
        if foot.to_centimeters() < meter {
            "smaller"
        } else {
            "bigger"
        };

    println!("One foot is {} than one meter.", cmp);
}

参照

derive

Returning Traits with dyn

The Rust compiler needs to know how much space every function's return type requires. This means all your functions have to return a concrete type. Unlike other languages, if you have a trait like Animal, you can't write a function that returns Animal, because its different implementations will need different amounts of memory.

However, there's an easy workaround. Instead of returning a trait object directly, our functions return a Box which contains some Animal. A box is just a reference to some memory in the heap. Because a reference has a statically-known size, and the compiler can guarantee it points to a heap-allocated Animal, we can return a trait from our function!

Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the dyn keyword, e.g. Box<dyn Animal>.

struct Sheep {}
struct Cow {}

trait Animal {
    // Instance method signature
    fn noise(&self) -> &'static str;
}

// Implement the `Animal` trait for `Sheep`.
impl Animal for Sheep {
    fn noise(&self) -> &'static str {
        "baaaaah!"
    }
}

// Implement the `Animal` trait for `Cow`.
impl Animal for Cow {
    fn noise(&self) -> &'static str {
        "moooooo!"
    }
}

// Returns some struct that implements Animal, but we don't know which one at compile time.
fn random_animal(random_number: f64) -> Box<dyn Animal> {
    if random_number < 0.5 {
        Box::new(Sheep {})
    } else {
        Box::new(Cow {})
    }
}

fn main() {
    let random_number = 0.234;
    let animal = random_animal(random_number);
    println!("You've randomly chosen an animal, and it says {}", animal.noise());
}

演算子のオーバーロード

Rustでは、多くの演算子はトレイトによってオーバーロードすることができます。つまり、一部の演算子は引数となる値の型に応じて異なる役割を果たすことができるということです。これが可能なのは、演算子が実際にはメソッド呼び出しの糖衣構文にすぎないからです。例えばa + bにおける+演算子はaddメソッドを(a.add(b)の形で)呼び出します。このaddメソッドはAddトレイトの一部です。それ故、+Addトレイトを実装している全ての型に対して有効なのです。

Addなどの、演算子をオーバーロードするトレイトの一覧はcore::opsにあります。

use std::ops;

struct Foo;
struct Bar;

#[derive(Debug)]
struct FooBar;

#[derive(Debug)]
struct BarFoo;

// The `std::ops::Add` trait is used to specify the functionality of `+`.
// Here, we make `Add<Bar>` - the trait for addition with a RHS of type `Bar`.
// The following block implements the operation: Foo + Bar = FooBar
// `std::ops::Add`トレイトは`+`の振る舞いを規定するために使用される
// ここでは`Foo`に対して`Add<Bar>`を実装する。これは加算時の右辺が`Bar`型
// の時に呼び出されるトレイト。つまり以下は`Foo + Bar = FooBar`という振る舞いを
// もたらす。
impl ops::Add<Bar> for Foo {
    type Output = FooBar;

    fn add(self, _rhs: Bar) -> FooBar {
        println!("> Foo.add(Bar) was called");

        FooBar
    }
}

// By reversing the types, we end up implementing non-commutative addition.
// Here, we make `Add<Foo>` - the trait for addition with a RHS of type `Foo`.
// This block implements the operation: Bar + Foo = BarFoo
// 型を反転することで、非可換の加算を実装できる。ここでは`Bar`に対して
// `Add<Foo>`を実装する。これは加算時の右辺が`Foo`型の時に呼び出されるメソッド。
// つまり以下は`Bar + Foo = BarFoo`という結果をもたらす。
impl ops::Add<Foo> for Bar {
    type Output = BarFoo;

    fn add(self, _rhs: Foo) -> BarFoo {
        println!("> Bar.add(Foo) was called");

        BarFoo
    }
}

fn main() {
    println!("Foo + Bar = {:?}", Foo + Bar);
    println!("Bar + Foo = {:?}", Bar + Foo);
}

See Also

Add, 構文の索引

メモリ解放

Dropトレイトにはメソッドが一つだけしかありません。dropです。これは、オブジェクトがスコープから抜けた時に自動で呼ばれます。Dropトレイトの主な使用目的は、インスタンスが所有する資源を開放することです。

Dropトレイトを実装している型の例としてはBoxVecStringFileProcess等があげられます。Dropトレイトは任意の型に対して手動で実装することができます。

以下の例ではdropメソッドにコンソールへの出力を追加することで、dropが呼ばれたタイミングが分かるようにしています。

struct Droppable {
    name: &'static str,
}

// This trivial implementation of `drop` adds a print to console.
// このちょっとした実装で、`drop`にコンソール出力機能がつきます。
impl Drop for Droppable {
    fn drop(&mut self) {
        println!("> Dropping {}", self.name);
    }
}

fn main() {
    let _a = Droppable { name: "a" };

    // block A
    {
        let _b = Droppable { name: "b" };

        // block B
        {
            let _c = Droppable { name: "c" };
            let _d = Droppable { name: "d" };

            println!("Exiting block B");
        }
        println!("Just exited block B");

        println!("Exiting block A");
    }
    println!("Just exited block A");

    // Variable can be manually dropped using the `drop` function
    // `drop`関数を用いて変数を手動で開放することもできます。
    drop(_a);
    // TODO ^ Try commenting this line
    // TODO ^ この行をコメントアウトしてみましょう。

    println!("end of the main function");

    // `_a` *won't* be `drop`ed again here, because it already has been
    // (manually) `drop`ed
    // `_a`はここで`drop`されることは *ない* 。なぜならば、上ですでに
    // (手動で)`drop`されているため。
}

イテレータ

Iteratorトレイトは、例えば配列のような、要素の集合に対してイテレータを実装するためのトレイトです。

このトレイトはnextの要素に相当するものを決定するためのメソッドのみを要求します。このメソッドはimplブロック内で手動で実装するか、あるいは(配列やrangeのように)自動で定義されます。

サッとイテレータを使いたい時は、for文で集合からイテレータを作成することが良くあります。これは.into_iter()メソッドを呼び出しています。

struct Fibonacci {
    curr: u32,
    next: u32,
}

// Implement `Iterator` for `Fibonacci`.
// The `Iterator` trait only requires a method to be defined for the `next` element.
// `Iterator`を`Fibonacci`に対して実装する。
// `Iterator`トレイトは次(`next`)の要素を取得するメソッドの定義だけを要求する。
impl Iterator for Fibonacci {
    // We can refer to this type using Self::Item
    type Item = u32;
    
    // Here, we define the sequence using `.curr` and `.next`.
    // The return type is `Option<T>`:
    //     * When the `Iterator` is finished, `None` is returned.
    //     * Otherwise, the next value is wrapped in `Some` and returned.
    // We use Self::Item in the return type, so we can change
    // the type without having to update the function signatures.
    // ここではイテレーションの流れを`.curr`と`.next`を使用して定義している。
    // 返り値の型は`Option<T>`で、これは:
    //     * `Iterator`が終了した時は`None`を返し、
    //     * そうでなければ`Some`でラップされた値を返す。
    fn next(&mut self) -> Option<Self::Item> {
        let new_next = self.curr + self.next;

        self.curr = self.next;
        self.next = new_next;

        // Since there's no endpoint to a Fibonacci sequence, the `Iterator` 
        // will never return `None`, and `Some` is always returned.
        // フィボナッチ数列には終端がないので、`Iterator`は決して
        // `None`を返さず、常に`Some`が返される。
        Some(self.curr)
    }
}

// Returns a Fibonacci sequence generator
// フィボナッチ数列のジェネレータを返す。
fn fibonacci() -> Fibonacci {
    Fibonacci { curr: 0, next: 1 }
}

fn main() {
    // `0..3` is an `Iterator` that generates: 0, 1, and 2.
    // `0..3`は0, 1, 2をジェネレートする`Iterator`
    let mut sequence = 0..3;

    println!("Four consecutive `next` calls on 0..3");
    println!("> {:?}", sequence.next());
    println!("> {:?}", sequence.next());
    println!("> {:?}", sequence.next());
    println!("> {:?}", sequence.next());

    // `for` works through an `Iterator` until it returns `None`.
    // Each `Some` value is unwrapped and bound to a variable (here, `i`).
    // `for`は`None`を返すまで、イテレータを舐めていき、出てきた`Some`を
    // アンラップして変数(ここでは`i`)に束縛する。
    println!("Iterate through 0..3 using `for`");
    for i in 0..3 {
        println!("> {}", i);
    }

    // The `take(n)` method reduces an `Iterator` to its first `n` terms.
    // `take(n)`メソッドは`Iterator`をはじめから`n`番目の要素までの部分に減らす。
    println!("The first four terms of the Fibonacci sequence are: ");
    for i in fibonacci().take(4) {
        println!("> {}", i);
    }

    // The `skip(n)` method shortens an `Iterator` by dropping its first `n` terms.
    // `skip(n)`メソッドは`Iterator`のはじめから`n`番目までの要素をとばす。
    println!("The next four terms of the Fibonacci sequence are: ");
    for i in fibonacci().skip(4).take(4) {
        println!("> {}", i);
    }

    let array = [1u32, 3, 3, 7];

    // The `iter` method produces an `Iterator` over an array/slice.
    // `iter`メソッドは配列やスライスからイテレータを作成する。
    println!("Iterate the following array {:?}", &array);
    for i in array.iter() {
        println!("> {}", i);
    }
}

impl Trait

impl Trait can be used in two locations:

  1. as an argument type
  2. as a return type

As an argument type

If your function is generic over a trait but you don't mind the specific type, you can simplify the function declaration using impl Trait as the type of the argument.

For example, consider the following code:

fn parse_csv_document<R: std::io::BufRead>(src: R) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
        .map(|line| {
            // For each line in the source
            line.map(|line| {
                // If the line was read successfully, process it, if not, return the error
                line.split(',') // Split the line separated by commas
                    .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace
                    .collect() // Collect all strings in a row into a Vec<String>
            })
        })
        .collect() // Collect all lines into a Vec<Vec<String>>
}

parse_csv_document is generic, allowing it to take any type which implements BufRead, such as BufReader<File> or [u8], but it's not important what type R is, and R is only used to declare the type of src, so the function can also be written an

fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result<Vec<Vec<String>>> {
    src.lines()
        .map(|line| {
            // For each line in the source
            line.map(|line| {
                // If the line was read successfully, process it, if not, return the error
                line.split(',') // Split the line separated by commas
                    .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace
                    .collect() // Collect all strings in a row into a Vec<String>
            })
        })
        .collect() // Collect all lines into a Vec<Vec<String>>
}

Note that using impl Trait as an argument type means that you cannot explicitly state what form of the function you use, i.e. parse_csv_document::<std::io::Empty>(std::io::empty()) will not work with the second example

As a return type

If your function returns a type that implements MyTrait, you can write its return type as -> impl MyTrait. This can help simplify your type signatures quite a lot!

use std::iter;
use std::vec::IntoIter;

// This function combines two `Vec<i32>` and returns an iterator over it.
// Look how complicated its return type is!
fn combine_vecs_explicit_return_type(
    v: Vec<i32>,
    u: Vec<i32>,
) -> iter::Cycle<iter::Chain<IntoIter<i32>, IntoIter<i32>>> {
    v.into_iter().chain(u.into_iter()).cycle()
}

// This is the exact same function, but its return type uses `impl Trait`.
// Look how much simpler it is!
fn combine_vecs(
    v: Vec<i32>,
    u: Vec<i32>,
) -> impl Iterator<Item=i32> {
    v.into_iter().chain(u.into_iter()).cycle()
}

fn main() {
    let v1 = vec![1, 2, 3];
    let v2 = vec![4, 5];
    let mut v3 = combine_vecs(v1, v2);
    assert_eq!(Some(1), v3.next());
    assert_eq!(Some(2), v3.next());
    assert_eq!(Some(3), v3.next());
    assert_eq!(Some(4), v3.next());
    assert_eq!(Some(5), v3.next());
    println!("all done");
}

More importantly, some Rust types can't be written out. For example, every closure has its own unnamed concrete type. Before impl Trait syntax, you had to allocate on the heap in order to return a closure. But now you can do it all statically, like this:

// Returns a function that adds `y` to its input
fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 {
    let closure = move |x: i32| { x + y };
    closure
}

fn main() {
    let plus_one = make_adder_function(1);
    assert_eq!(plus_one(2), 3);
}

You can also use impl Trait to return an iterator that uses map or filter closures! This makes using map and filter easier. Because closure types don't have names, you can't write out an explicit return type if your function returns iterators with closures. But with impl Trait you can do this easily:

fn double_positives<'a>(numbers: &'a Vec<i32>) -> impl Iterator<Item = i32> + 'a {
    numbers
        .iter()
        .filter(|x| x > &&0)
        .map(|x| x * 2)
}

fn main() {
    let singles = vec![-3, -2, 2, 3];
    let doubles = double_positives(&singles);
    assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]);
}

クローン

メモリ上の資源を扱う際、変数束縛や関数呼び出しを介して移動させるのがデフォルトの挙動です。しかしながら、場合によっては資源のコピーを作るのが適切なこともあります。

Cloneトレイトはまさにこのためにあります。普通はCloneトレイトで定義されている.clone()を用います。

// A unit struct without resources
// いかなる資源も持たない構造体
#[derive(Debug, Clone, Copy)]
struct Unit;

// A tuple struct with resources that implements the `Clone` trait
// `Clone`トレイトを実装する型の変数を資源として持つタプル
#[derive(Clone, Debug)]
struct Pair(Box<i32>, Box<i32>);

fn main() {
    // Instantiate `Unit`
    // `Unit`のインスタンスを作成
    let unit = Unit;
    // Copy `Unit`, there are no resources to move
    // `Unit`をコピー、移動させる資源は存在しない
    let copied_unit = unit;

    // Both `Unit`s can be used independently
    // いずれの`Unit`も独立に使用できる。
    println!("original: {:?}", unit);
    println!("copy: {:?}", copied_unit);

    // Instantiate `Pair`
    // `Pair`のインスタンスを作成
    let pair = Pair(Box::new(1), Box::new(2));
    println!("original: {:?}", pair);

    // Move `pair` into `moved_pair`, moves resources
    // `pair`を`moved_pair`に移動、資源は移動(`move`)する。
    let moved_pair = pair;
    println!("moved: {:?}", moved_pair);

    // Error! `pair` has lost its resources
    // エラー! `pair`は資源を失っている。
    //println!("original: {:?}", pair);
    // TODO ^ Try uncommenting this line

    // TODO ^ この行をアンコメントしてみましょう。
    // Clone `moved_pair` into `cloned_pair` (resources are included)
    // `moved_pair`を`cloned_pair`にクローンする。(資源もクローンされる。)
    let cloned_pair = moved_pair.clone();
    // Drop the original pair using std::mem::drop
    // std::mem::dropを用いて元のpairをドロップする
    drop(moved_pair);

    // Error! `moved_pair` has been dropped
    // エラー! `moved_pair`はドロップされている。
    //println!("copy: {:?}", moved_pair);
    // TODO ^ Try uncommenting this line
    // TODO ^ この行をアンコメントしてみましょう。

    // The result from .clone() can still be used!
    // .clone()した値はまだ使用可能!
    println!("clone: {:?}", cloned_pair);
}

Supertraits

Rust doesn't have "inheritance", but you can define a trait as being a superset of another trait. For example:

trait Person {
    fn name(&self) -> String;
}

// Person is a supertrait of Student.
// Implementing Student requires you to also impl Person.
trait Student: Person {
    fn university(&self) -> String;
}

trait Programmer {
    fn fav_language(&self) -> String;
}

// CompSciStudent (computer science student) is a subtrait of both Programmer 
// and Student. Implementing CompSciStudent requires you to impl both supertraits.
trait CompSciStudent: Programmer + Student {
    fn git_username(&self) -> String;
}

fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
    format!(
        "My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
        student.name(),
        student.university(),
        student.fav_language(),
        student.git_username()
    )
}

fn main() {}

参照

The Rust Programming Language chapter on supertraits

Disambiguating overlapping traits

A type can implement many different traits. What if two traits both require the same name? For example, many traits might have a method named get(). They might even have different return types!

Good news: because each trait implementation gets its own impl block, it's clear which trait's get method you're implementing.

What about when it comes time to call those methods? To disambiguate between them, we have to use Fully Qualified Syntax.

trait UsernameWidget {
    // Get the selected username out of this widget
    fn get(&self) -> String;
}

trait AgeWidget {
    // Get the selected age out of this widget
    fn get(&self) -> u8;
}

// A form with both a UsernameWidget and an AgeWidget
struct Form {
    username: String,
    age: u8,
}

impl UsernameWidget for Form {
    fn get(&self) -> String {
        self.username.clone()
    }
}

impl AgeWidget for Form {
    fn get(&self) -> u8 {
        self.age
    }
}

fn main() {
    let form = Form{
        username: "rustacean".to_owned(),
        age: 28,
    };

    // If you uncomment this line, you'll get an error saying 
    // "multiple `get` found". Because, after all, there are multiple methods
    // named `get`.
    // println!("{}", form.get());

    let username = <Form as UsernameWidget>::get(&form);
    assert_eq!("rustacean".to_owned(), username);
    let age = <Form as AgeWidget>::get(&form);
    assert_eq!(28, age);
}

参照

The Rust Programming Language chapter on Fully Qualified syntax

macro_rules!

Rustはメタプログラミングを可能にする、パワフルなマクロシステムを備えています。これまで見てきたように、マクロは!で終わることを除けば関数のように見えます。関数と違うのは関数呼び出し(function call)を生成する代わりに、ソースコード中に展開され、周囲のプログラムとともにコンパイルされる点です。

しかし、Cやその他の言語のマクロが文字列のプリプロセッシングをするのと異なり、Rustのマクロは抽象構文木へと展開されるので、予期せぬprecendece(演算子の優先順位)のバグに出くわすことがありません。

マクロを作成するにはmacro_rules!というマクロを使用します。

// This is a simple macro named `say_hello`.
// `say_hello`という名のシンプルなマクロ
macro_rules! say_hello {
    // `()` indicates that the macro takes no argument.
    // `()`はマクロが引数をとらないことを示す。
    () => {
        // The macro will expand into the contents of this block.
        // マクロは(訳注: プリコンパイルの段階で)このブロック内の内容に展開されます。
        println!("Hello!");
    };
}

fn main() {
    // This call will expand into `println!("Hello");`
    // この呼び出しは`println!("Hello");`に置き換えられます。
    say_hello!()
}

ではどうしてマクロは便利なのでしょうか?

  1. 同じことを繰り返し書いてはいけない (Don't repeat yourself) から。 複数の場所で、別の型だけれど似たような機能が必要な時がよくあります。 しばしば、マクロはコードを繰り返し書くのを避ける有用な手段なのです(あとで詳述)。
  1. ドメイン特化言語であるから。マクロを使うと、特定の目的のための特定の構文を定義することができます(あとで詳述)。
  1. 可変個引数によるインターフェース。 取る引数の数が可変であるようなインターフェースを定義したくなることもあるでしょう。 例えば、println!は、フォーマット文字列に依存した任意の数の引数を取ることができます(あとで詳述)!

構文

以下のサブセクションでは、Rustにおいてマクロを定義する方法を示します。 3つの基本的な考え方があります:

識別子

macroの引数は$が頭につきます。型は 識別子 (designator)でアノテーションされます。

macro_rules! create_function {
    // This macro takes an argument of designator `ident` and
    // creates a function named `$func_name`.
    // The `ident` designator is used for variable/function names.
    // このマクロは`ident`識別子に対応する値を引数として取り
    // `$func_name`という名の関数を作成する。
    // `ident`識別子は関数・変数の名前用の識別子である。
    ($func_name:ident) => {
        fn $func_name() {
            // The `stringify!` macro converts an `ident` into a string.
            // `stringify!`というマクロは`ident`を文字列に変える。
            println!("You called {:?}()",
                     stringify!($func_name));
        }
    };
}

// Create functions named `foo` and `bar` with the above macro.
// 上のマクロを利用して`foo`、`bar`という名の関数を作成する。
create_function!(foo);
create_function!(bar);

macro_rules! print_result {
    // This macro takes an expression of type `expr` and prints
    // it as a string along with its result.
    // The `expr` designator is used for expressions.
    // このマクロは`expr`識別子に対応する値を引数として取り、
    // その結果を文字列としてプリントする。
    // `expr`識別子は式に対応する。
    ($expression:expr) => {
        // `stringify!` will convert the expression *as it is* into a string.
        // `stringify!`は式を *そのままの形で* 文字列に変換する
        println!("{:?} = {:?}",
                 stringify!($expression),
                 $expression);
    };
}

fn main() {
    foo();
    bar();

    print_result!(1u32 + 1);

    // Recall that blocks are expressions too!
    // ブロックも式の一種であることを思い出しましょう!
    print_result!({
        let x = 1u32;

        x * x + 2 * x - 1
    });
}

使用できる識別子には以下のようなものがあります。

  • block
  • expr 式に使用
  • ident 関数、変数の名前に使用
  • item
  • literal はリテラル定数(訳注:文字だけではない。Literal expressionsを参照)に使用
  • pat (パターン)
  • path
  • stmt (宣言)
  • tt (トークンツリー)
  • ty ()
  • vis (可視性修飾子)(訳注:pub (crate)とか)

完全なリストを見るには、Rustリファレンスを読んでください。

オーバーロード

マクロは異なる引数の組み合わせを取るようにオーバーロードすることができるため、macro_rules!はマッチと似たような使い方をすることができます。

// `test!` will compare `$left` and `$right`
// in different ways depending on how you invoke it:
// `test!`は`$left`と`$right`を異なる呼び出し方に応じて
// 比較する。
macro_rules! test {
    // Arguments don't need to be separated by a comma.
    // Any template can be used!
    // 引数はカンマで区切らなくてもよい
    // テンプレートの形態は自由!
    ($left:expr; and $right:expr) => {
        println!("{:?} and {:?} is {:?}",
                 stringify!($left),
                 stringify!($right),
                 $left && $right)
    };
    // ^ each arm must end with a semicolon.
    // それぞれの`=>`節はセミコロンで終わる必要がある。
    ($left:expr; or $right:expr) => {
        println!("{:?} or {:?} is {:?}",
                 stringify!($left),
                 stringify!($right),
                 $left || $right)
    };
}

fn main() {
    test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
    test!(true; or false);
}

繰り返し

マクロは引数のリストの中で+を使うことができ、そうすることによって、引数が少なくとも1回以上繰り返されるということを示すことができます。同様に*の場合は、0以上を示します。

以下の例では、マッチ対象を $(...),+で囲むことにより、カンマで区切られた1つ以上の式とマッチします。最後のセミコロンは必須ではないことに注目しましょう。

// `find_min!` will calculate the minimum of any number of arguments.
// `find_min!`は引数として与えられた数字の中の最低の値を計算する。
macro_rules! find_min {
    // Base case:
    // 基本となるケース
    ($x:expr) => ($x);
    // `$x` followed by at least one `$y,`
    // `$x`に少なくとも1つの`$y`が続く場合
    ($x:expr, $($y:expr),+) => (
        // Call `find_min!` on the tail `$y`
        // `find_min!`を残りの`$y`に対して再帰的に適用
        std::cmp::min($x, find_min!($($y),+))
    )
}

fn main() {
    println!("{}", find_min!(1));
    println!("{}", find_min!(1 + 2, 2));
    println!("{}", find_min!(5, 2 * 3, 4));
}

DRY (Don't Repeat Yourself)

マクロは関数やテストなどにおいて、共通の部分を抽出することでDRYなコードを書くのに役立ちます。ここでは+=*=-=Vec<T>を実装、テストするにあたって、マクロがどのように役立つかを見ていきます。

use std::ops::{Add, Mul, Sub};

macro_rules! assert_equal_len {
    // The `tt` (token tree) designator is used for
    // operators and tokens.
    // `tt` (トークン木)識別子は演算子とトークン用の識別子です。
    ($a:expr, $b:expr, $func:ident, $op:tt) => {
        assert!($a.len() == $b.len(),
                "{:?}: dimension mismatch: {:?} {:?} {:?}",
                stringify!($func),
                ($a.len(),),
                stringify!($op),
                ($b.len(),));
    };
}

macro_rules! op {
    ($func:ident, $bound:ident, $op:tt, $method:ident) => {
        fn $func<T: $bound<T, Output=T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
            assert_equal_len!(xs, ys, $func, $op);

            for (x, y) in xs.iter_mut().zip(ys.iter()) {
                *x = $bound::$method(*x, *y);
                // *x = x.$method(*y);
            }
        }
    };
}

// Implement `add_assign`, `mul_assign`, and `sub_assign` functions.
// `add_assign`、`mul_assign`、`sub_assign`、関数を実装
op!(add_assign, Add, +=, add);
op!(mul_assign, Mul, *=, mul);
op!(sub_assign, Sub, -=, sub);

mod test {
    use std::iter;
    macro_rules! test {
        ($func:ident, $x:expr, $y:expr, $z:expr) => {
            #[test]
            fn $func() {
                for size in 0usize..10 {
                    let mut x: Vec<_> = iter::repeat($x).take(size).collect();
                    let y: Vec<_> = iter::repeat($y).take(size).collect();
                    let z: Vec<_> = iter::repeat($z).take(size).collect();

                    super::$func(&mut x, &y);

                    assert_eq!(x, z);
                }
            }
        };
    }

    // Test `add_assign`, `mul_assign`, and `sub_assign`.
    // `add_assign`と`mul_assign`と`sub_assign`をテスト
    test!(add_assign, 1u32, 2u32, 3u32);
    test!(mul_assign, 2u32, 3u32, 6u32);
    test!(sub_assign, 3u32, 2u32, 1u32);
}
$ rustc --test dry.rs && ./dry
running 3 tests
test test::mul_assign ... ok
test test::add_assign ... ok
test test::sub_assign ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured

Domain Specific Languages (ドメイン特化言語、DSLs)

DSLとはRustマクロに埋め込まれた小さな「言語」のことです。 マクロ機能は通常のRustのプログラムへと展開されるので、これは完全に正当なRustなのですが、まるで小さな言語であるかのように見えます。 これにより、(一定の条件のもとで)なんらかの特定の機能のための簡潔・直感的な構文を定義することができるようになります。

ちょっとした計算機APIを定義したいとしましょう。 式を与えると、出力がコンソールに書き出されるようにしたいです。

macro_rules! calculate {
    (eval $e:expr) => {{
        {
            let val: usize = $e; // Force types to be integers
                                 // 型を整数に制約
            println!("{} = {}", stringify!{$e}, val);
        }
    }};
}

fn main() {
    calculate! {
        eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
                   // `eval`はRustのキーワード *じゃない* よね!
    }

    calculate! {
        eval (1 + 2) * (3 / 4)
    }
}

出力はこうなります:

1 + 2 = 3
(1 + 2) * (3 / 4) = 0

これはとても単純な例ですが、lazy_staticclapのように、もっと複雑なインターフェースも開発されています。

また、マクロの中に2組の括弧があることにも注目してください。 外側のは、()[]に加え、macro_rules!の構文の一部です。

可変個引数によるインターフェース

可変個引数のインターフェースとは、任意の数の引数を取るものです。 例えば、println!は、フォーマット文字列の定義に従い、任意の数の引数を取ることができます。

前のセクションのcalculate!マクロを、可変個引数に拡張することができます:

macro_rules! calculate {
    // 単一の`eval`のためのパターン
    // The pattern for a single `eval`
    (eval $e:expr) => {{
        {
            let val: usize = $e; // Force types to be integers
            println!("{} = {}", stringify!{$e}, val);
        }
    }};

    // 複数の`eval`を再帰的に分解する
    // Decompose multiple `eval`s recursively
    (eval $e:expr, $(eval $es:expr),+) => {{
        calculate! { eval $e }
        calculate! { $(eval $es),+ }
    }};
}

fn main() {
                 // ほら!可変な`calculate!`だよ!
    calculate! { // Look ma! Variadic `calculate!`!
        eval 1 + 2,
        eval 3 + 4,
        eval (2 * 3) + 1
    }
}

出力:

1 + 2 = 3
3 + 4 = 7
(2 * 3) + 1 = 7

エラーハンドリング

エラーハンドリングとは失敗の起きる可能性を扱うプロセスのことです。例えば、ファイルを読み込むのに失敗した際、その 誤った インプットを使い続けるのは明らかに問題です。そのようなエラーを通知して明示的に扱うことで、残りのプログラムに問題が波及することを防ぐことができるようになります。

Rustには、これからこの章で見ていく通り、エラーを処理するための様々な方法が存在します。それらは全て僅かに異なり、ユースケースも異なります。経験則として:

明示的なpanicはテストや復旧不可能なエラーに対して効果的です。プロトタイプにも便利で、例えば未実装の関数を扱う時などに有効ですが、このような場合にはより叙述的なunimplementedの方が良いでしょう。テストにおいてはpanicは明示的にテストを失敗させるための良い手法になるでしょう。

Option型は値があるとは限らない場合や、値が無いことがエラーの条件とならない場合に有効です。例えば親ディレクトリ(/C:はそれを持ちません)などです。Optionを扱う際は、unwrapがプロトタイプや値が確実に存在することが約束されるケースに使えます。しかし、expectの方が何かが上手くいかなかった際にエラーメッセージを指定することができるため、より便利でしょう。

何かが上手くいかない可能性があったり、呼び出し元が問題を処理しなければならない時は、Resultを使いましょう。unwrapexpectを実行することもできます(テストや短期的なプロトタイプ以外では使わないでください)。

より詳細なエラーハンドリングに関する議論については、オフィシャルブックの該当の章を参考にしてください。

訳注: こちらのQiitaの日本語記事も参考になります。「RustでOption値やResult値を上手に扱う」

panic

panicは、最もシンプルなエラーハンドリングの仕組みです。エラーメッセージの出力、スタックの巻き戻し、そして多くの場合プログラムの終了を実行します。 例として、エラー条件に対して明示的にpanicを呼び出してみましょう。

fn drink(beverage: &str) {
    // You shouldn't drink too much sugary beverages.
    // Princesses hate snakes, so we need to stop if she disapproves!
    // 甘すぎる飲み物を飲むべきではありません。
    if beverage == "lemonade" { panic!("AAAaaaaa!!!!"); }

    println!("Some refreshing {} is all I need.", beverage);
}

fn main() {
    drink("water");
    drink("lemonade");
}

Optionunwrap

以前の例では、甘いレモネードを飲んだ際にpanicを呼び出すことによって、自由にプログラムの実行を失敗させられることが分かりました。では、何らかの飲み物を期待しているにもかかわらず、何も受け取らなかったらどうなるでしょう?これは悲惨なケースになるので、エラーハンドリングする必要があります!

このケースに対して、レモネードと同じように、空文字列("")と比較することもできますが、せっかくRustを使っているので、その代わりにコンパイラに飲み物がないケースを指摘させてみましょう。

stdライブラリの中の、Option<T>と呼ばれるenumは、任意の型Tである変数の値が存在しない可能性がある場合に用いられます。値の状態によって、下記2つのパターンのうちの1つとして扱われます。

  • Some(T): 型Tの値がある場合
  • None: 値が存在しない場合。

これらはmatchを用いて明示的に扱うこともできますし、unwrapで暗黙に処理することもできます。後者はSomeの中の値を返すかpanicするかのどちらかです。

expectメソッドを用いて、panicを手動でカスタマイズできることに触れておきましょう。これは(unwrapをそのまま用いた場合よりも)内容が理解しやすいエラーメッセージを出力するのに役立ちます。次の例では、結果をより明示的に、可能ならいつでもpanicできるように扱っていきます。

// The adult has seen it all, and can handle any drink well.
// All drinks are handled explicitly using `match`.
// 大人は経験豊富なので、大体どんな飲み物にも対処できます。
// あらゆる飲み物は`match`を用いて手動で処理されます。
fn give_adult(drink: Option<&str>) {
    // Specify a course of action for each case.
    match drink {
        Some("lemonade") => println!("Yuck! Too sugary."),
        Some(inner)   => println!("{}? How nice.", inner),
        None          => println!("No drink? Oh well."),
    }
}

// Others will `panic` before drinking sugary drinks.
// All drinks are handled implicitly using `unwrap`.
// 他の人たちは甘い飲み物を飲む前に`panic`します。
// 全ての飲み物は`unwrap`を使って暗黙的に処理されます。
fn drink(drink: Option<&str>) {
    // `unwrap` returns a `panic` when it receives a `None`.
    // `unwrap`を使用すると値が`None`だった際に`panic`を返します。
    let inside = drink.unwrap();
    if inside == "lemonade" { panic!("AAAaaaaa!!!!"); }

    println!("I love {}s!!!!!", inside);
}

fn main() {
    let water  = Some("water");
    let lemonade = Some("lemonade");
    let void  = None;

    give_adult(water);
    give_adult(lemonade);
    give_adult(void);

    let coffee = Some("coffee");
    let nothing = None;

    drink(coffee);
    drink(nothing);
}

?によるOptionのアンパック

Optionをアンパックするにはmatch文を使うこともできますが、?を使う方が簡単になることが多いでしょう。Optionxがあるとすると、x?を評価した値は、xSomeの場合はxに格納された値となり、そうでなければ実行中の関数を終了させ、Noneを返します。

fn next_birthday(current_age: Option<u8>) -> Option<String> {
	// If `current_age` is `None`, this returns `None`.
	// If `current_age` is `Some`, the inner `u8` gets assigned to `next_age`
    // `current_age`が`None`の場合、`None`を返す。
    // `current_age`が`Some`の場合、内部の`u8`型の値が`next_age`に代入される。
    let next_age: u8 = current_age?;
    Some(format!("Next year I will be {}", next_age))
}

多くの?を共に使うことで、リーダブルなコードを書くことができます。

struct Person {
    job: Option<Job>,
}

#[derive(Clone, Copy)]
struct Job {
    phone_number: Option<PhoneNumber>,
}

#[derive(Clone, Copy)]
struct PhoneNumber {
    area_code: Option<u8>,
    number: u32,
}

impl Person {

    // Gets the area code of the phone number of the person's job, if it exists.
    // その人の市外局番が存在する場合、取得する。
    fn work_phone_area_code(&self) -> Option<u8> {
        // This would need many nested `match` statements without the `?` operator.
        // It would take a lot more code - try writing it yourself and see which
        // is easier.
        // `?`がなければ、多くのネストされた`match`文を必要とするため、より長いコードとなる。
        // 実際に書いて、どちらの方が簡単か確かめてみましょう。
        self.job?.phone_number?.area_code
    }
}

fn main() {
    let p = Person {
        job: Some(Job {
            phone_number: Some(PhoneNumber {
                area_code: Some(61),
                number: 439222222,
            }),
        }),
    };

    assert_eq!(p.work_phone_area_code(), Some(61));
}

Combinators: map

matchOptionを扱うのに適したメソッドです。しかし、大量にこれを使用しているとじきに億劫になってくるでしょう。引数の値が有効である(訳注: この場合はNoneでない)必要がある関数を扱う際には特にそうです。 In these cases, combinators can be used to manage control flow in a modular fashion.

Some -> SomeあるいはNone -> Noneの単純な操作を適用する必要がある場合には、Optionmap()というビルトインのメソッドを提供していますので、これを使用しましょう。 map()のフレキシビリティは、複数のmap()をチェインしなければならない場合にさらに際立ちます。

以下の例では、process()が直前の関数全てを用いた場合と同じ機能を、よりコンパクトに果たしているのがわかります。

#![allow(dead_code)]

#[derive(Debug)] enum Food { Apple, Carrot, Potato }

#[derive(Debug)] struct Peeled(Food);
#[derive(Debug)] struct Chopped(Food);
#[derive(Debug)] struct Cooked(Food);

// Peeling food. If there isn't any, then return `None`.
// Otherwise, return the peeled food.
// 食べ物の皮をむく。存在しない場合は単純に`None`を返す。
// そうでなければ皮を向いた食べ物を返す。
fn peel(food: Option<Food>) -> Option<Peeled> {
    match food {
        Some(food) => Some(Peeled(food)),
        None       => None,
    }
}

// Chopping food. If there isn't any, then return `None`.
// Otherwise, return the chopped food.
// 上と同じように、食べ物を切る前に、皮を向いた食べ物の有無を知る必要がある。
fn chop(peeled: Option<Peeled>) -> Option<Chopped> {
    match peeled {
        Some(Peeled(food)) => Some(Chopped(food)),
        None               => None,
    }
}

// Cooking food. Here, we showcase `map()` instead of `match` for case handling.
// 上のチェックと同様だが`match`の代わりに`map()`を使用している。
fn cook(chopped: Option<Chopped>) -> Option<Cooked> {
    chopped.map(|Chopped(food)| Cooked(food))
}

// A function to peel, chop, and cook food all in sequence.
// We chain multiple uses of `map()` to simplify the code.
// 複数の`map()`をチェインさせて、上のプロセスをシンプルにすることもできる。
fn process(food: Option<Food>) -> Option<Cooked> {
    food.map(|f| Peeled(f))
        .map(|Peeled(f)| Chopped(f))
        .map(|Chopped(f)| Cooked(f))
}

// Check whether there's food or not before trying to eat it!
// 食べる前に、食べ物の有無をチェックするのは大事ですよね!
fn eat(food: Option<Cooked>) {
    match food {
        Some(food) => println!("Mmm. I love {:?}", food),
        None       => println!("Oh no! It wasn't edible."),
    }
}

fn main() {
    let apple = Some(Food::Apple);
    let carrot = Some(Food::Carrot);
    let potato = None;

    let cooked_apple = cook(chop(peel(apple)));
    let cooked_carrot = cook(chop(peel(carrot)));
    // Let's try the simpler looking `process()` now.
    // よりシンプルな見た目の`process()`を使用する。
    let cooked_potato = process(potato);

    eat(cooked_apple);
    eat(cooked_carrot);
    eat(cooked_potato);
}

参照

closures, Option, Option::map()

Combinators: and_then

先ほどはmap()を、チェイン構文を用いてmatch文を単純化する物として説明しました。しかしOption<T>を返す関数に対してのmap()の使用はネストしたOption<Option<T>>を生じさせます。ですので、複数の関数呼び出しをチェインさせることは混乱を招く場合があります。そんな時こそand_then()の出番です。他の言語ではflatmapと呼ばれることもあります。

and_then()は引数として与えられた関数にラップされた値を渡しますが、その値がNoneだった場合はNoneを返します。

以下の例ではcookable_v2()Option<Food>を返すため、and_then()ではなくmap()を使用すると最終的にOption<Option<Food>>になります。これはeat()には不適切な型です。

#![allow(dead_code)]

#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }

// We don't have the ingredients to make Sushi.
// 我々は寿司の材料を持っていない
fn have_ingredients(food: Food) -> Option<Food> {
    match food {
        Food::Sushi => None,
        _           => Some(food),
    }
}

// We have the recipe for everything except Cordon Bleu.
// コルドン・ブルー(Cordon Bleu)のレシピも持っていない。
fn have_recipe(food: Food) -> Option<Food> {
    match food {
        Food::CordonBleu => None,
        _                => Some(food),
    }
}

// To make a dish, we need both the recipe and the ingredients.
// We can represent the logic with a chain of `match`es:
// 料理を作るためには、材料とレシピの両方が必要。
// ロジックの流れを`match`のチェインで表す。
fn cookable_v1(food: Food) -> Option<Food> {
    match have_recipe(food) {
        None       => None,
        Some(food) => match have_ingredients(food) {
            None       => None,
            Some(food) => Some(food),
        },
    }
}

// This can conveniently be rewritten more compactly with `and_then()`:
// `and_then()`を用いることで、同じことをよりコンパクトに表現できる。
fn cookable_v2(food: Food) -> Option<Food> {
    have_recipe(food).and_then(have_ingredients)
}

fn eat(food: Food, day: Day) {
    match cookable_v2(food) {
        Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),
        None       => println!("Oh no. We don't get to eat on {:?}?", day),
    }
}

fn main() {
    let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);

    eat(cordon_bleu, Day::Monday);
    eat(steak, Day::Tuesday);
    eat(sushi, Day::Wednesday);
}

参照

closures, Option, Option::and_then()

Result

Resultは、リッチなバージョンのOption型で, 値の不在の可能性の代わりにエラーの可能性を示します。

つまり、Result<T, E>は以下の2つの結果を持ちます。

  • Ok<T>: 要素Tが見つかった場合
  • Err<E>: 要素Eとともにエラーが見つかった場合

慣例により、Okが期待される結果であり、Errは期待されない結果です。

Optionと同様、Resultは多くのメソッドを持ちます。例えばunwrap()は、Tもしくはpanicをもたらします。エラーハンドリングでは、ResultOptionで重複するコンビネータが多くあります。

Rustを書いていく中で、parse()メソッドなど、Result型を返すメソッドを目にするでしょう。文字列を他の型にパースすることは必ずしも成功する訳ではないため、Resultを返すことで失敗するケースについてもカバーできるのです。

早速、文字列をparse()した場合の成功例と失敗例を見てみましょう。

fn multiply(first_number_str: &str, second_number_str: &str) -> i32 {
    // Let's try using `unwrap()` to get the number out. Will it bite us?
    // `unwrap()`で数字を取り出してみましょう。痛い目を見るでしょうか?
    let first_number = first_number_str.parse::<i32>().unwrap();
    let second_number = second_number_str.parse::<i32>().unwrap();
    first_number * second_number
}

fn main() {
    let twenty = multiply("10", "2");
    println!("double is {}", twenty);

    let tt = multiply("t", "2");
    println!("double is {}", tt);
}

失敗例では、parse()がエラーを返すためunwrap()がパニックします。そして、panicはプログラムを終了させて不快なエラーメッセージを出力します。

エラーメッセージを改善するために、リターン型に対してもっと明確になるべきで、またエラーを明示的に処理することを考えるべきです。

main内で使うResult

Result型は、明示的な指定によりmain関数のリターン型にもなります。一般に、main関数は以下のような形になるでしょう。

fn main() {
    println!("Hello World!");
}

一方mainResultをリターン型とすることも可能です。エラーがmain関数内で発生した時、エラーコードを返し、エラーに関するデバッグ表記を(Debugトレートを使って)出力します。以下の例ではそのようなシナリオを示し、この先の節でカバーする内容に触れていきます。

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number_str = "10";
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };
    println!("{}", number);
    Ok(())
}

Resultmap

前の例で見たmultiplyでのパニックは、コードを強固にするためには書きません。一般に、呼び出した側がエラーをどのように対処するべきかを自由に決められるように、エラーを呼び出した場所に返すのが好ましいです。

まずは、どのようなエラー型を扱っているのかを知る必要があります。Err型を定めるために、i32に対しFromStrトレートを使って実装されたparse()を見てみましょう。結果、Err型はParseIntErrorというものであることが分かります。

以下の例では、単純なmatch文が全体として扱いづらいコードにしています。

use std::num::ParseIntError;

// With the return type rewritten, we use pattern matching without `unwrap()`.
// リターン型を再度記述することで、パターンマッチングを`unwrap()`なしで行います。
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    match first_number_str.parse::<i32>() {
        Ok(first_number)  => {
            match second_number_str.parse::<i32>() {
                Ok(second_number)  => {
                    Ok(first_number * second_number)
                },
                Err(e) => Err(e),
            }
        },
        Err(e) => Err(e),
    }
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    // This still presents a reasonable answer.
    // ここは以前と変わらず、妥当な解を与えます。
    let twenty = multiply("10", "2");
    print(twenty);

    // The following now provides a much more helpful error message.
    // こちらは今度は有益なエラーメッセージを与えます。
    let tt = multiply("t", "2");
    print(tt);
}

幸運にも、Optionmapand_then、その他多くのコンビネータもResultのために実装されています。Resultに全てのリストが記載されています。

use std::num::ParseIntError;

// As with `Option`, we can use combinators such as `map()`.
// This function is otherwise identical to the one above and reads:
// Modify n if the value is valid, otherwise pass on the error.
// `Option`と同様、`map()`などのコンビネータを使うことができます。
// この関数は`map()`を使っている点以外は上記の関数と同じで、
// 値が有効ならnを変更し、無効であればエラーをそのまま見送ります。
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    // This still presents a reasonable answer.
    // ここは以前と変わらず、妥当な解を与えます。
    let twenty = multiply("10", "2");
    print(twenty);

    // The following now provides a much more helpful error message.
    // こちらは今度は有益なエラーメッセージを与えます。
    let tt = multiply("t", "2");
    print(tt);
}

Resultに対するエイリアス

特定のResult型を何度も使いたくなるのはどんな時でしょう?Rustはエイリアスの作成をサポートしていたことを思い出してください。便利なことに、特定のResult型に対しても定義することができます。

モジュールレベルでは、エイリアスの作成は非常に役に立ちます。特定のモジュールで見られるエラーは同じErr型を持つため、単一のエイリアスで簡潔にResultsに関わる全てを定義できます。stdライブラリが提供するもの(io::Result)もあるほど有益なのです!

簡単な例で構文を見てみましょう。

use std::num::ParseIntError;

// Define a generic alias for a `Result` with the error type `ParseIntError`.
// `ParseIntError`を`Err`の型として持つ全ての`Result`のジェネリックエイリアス
type AliasedResult<T> = Result<T, ParseIntError>;

// Use the above alias to refer to our specific `Result` type.
// 上で定義したエイリアス(この場所特有の`Result`型)を使用
fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult<i32> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}

// Here, the alias again allows us to save some space.
// もう一度使用。エイリアスによって再度明記する必要性がない。
fn print(result: AliasedResult<i32>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("10", "2"));
    print(multiply("t", "2"));
}

参照

io::Result

早期リターン

前の例では、コンビネータの活用によりエラーを明示的に処理しました。場合分けに対する別の対処法として、match文と早期リターンを組み合わせて使うこともできます。

つまり、エラーが発生した時点で関数の実行を止め、エラーを返してしまうという単純な方法が使えるということです。この方法の方がより読みやすく書きやすい場合があります。早期リターンを使って実装された、前の例の新たなバージョンを考えてみましょう。

use std::num::ParseIntError;

fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    let first_number = match first_number_str.parse::<i32>() {
        Ok(first_number)  => first_number,
        Err(e) => return Err(e),
    };

    let second_number = match second_number_str.parse::<i32>() {
        Ok(second_number)  => second_number,
        Err(e) => return Err(e),
    };

    Ok(first_number * second_number)
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("10", "2"));
    print(multiply("t", "2"));
}

ここまでで、コンビネータと早期リターンによる明示的なエラーハンドリングについて学びました。しかし、パニックは一般に避けたいですが、全てのエラーを明示的に処理するのも厄介でしょう。

次の節では、panicを発生させずにunwrapする必要があるケースのための?について紹介していきます。

?の導入

時にはpanicの可能性を無視して、unwrapのシンプルさを活用したいこともあるでしょう。今までのunwrapは、値を取り出すためだけであろうとも、ネストを深く書くことを要求しました。そして、これがまさに?の目的です。

Errを見つけるにあたり、2つのとるべき行動があります。

  1. 可能な限り避けたいと決めたpanic!
  2. Errは処理できないことを意味するためreturn

?ほぼ1まさしく、Errに対してpanicするよりreturnするという点でunwrapと同等です。コンビネータを使った以前の例をどれだけ簡潔に書けるか見てみましょう。

use std::num::ParseIntError;

fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    let first_number = first_number_str.parse::<i32>()?;
    let second_number = second_number_str.parse::<i32>()?;

    Ok(first_number * second_number)
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("10", "2"));
    print(multiply("t", "2"));
}

try!マクロ

?ができる前、同様の動作をtry!マクロによって行うことができました。現在は?オペレータが推奨されていますが、古いコードではtry!に出会うこともあります。try!を使って前の例と同じmultiply関数を実装すると、以下のようになるでしょう。

// To compile and run this example without errors, while using Cargo, change the value 
// of the `edition` field, in the `[package]` section of the `Cargo.toml` file, to "2015".
// Cargoを使いながらこの例をエラーなくコンパイル、及び実行する場合、
// `Cargo.toml`ファイル内、`[package]`セクションの`edition`の値を"2015"に変更してください。

use std::num::ParseIntError;

fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    let first_number = try!(first_number_str.parse::<i32>());
    let second_number = try!(second_number_str.parse::<i32>());

    Ok(first_number * second_number)
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("10", "2"));
    print(multiply("t", "2"));
}
1

詳細はre-enter ?を参照。

複数のエラー型

Resultが他のResultと連携したり、Optionが他のOptionと連携するなど、今までの例はとても便利な物でした。

時にはOptionResultと連携したり、Result<T, Error1>Result<T, Error2>と連携する必要もあるでしょう。そのような場面では、異なるエラー型を構成しやすく、かつ連携しやすく管理したいです。

以下のコードはunwrapの2つのインスタンスが異なるエラー型を生成します。Vec::firstOptionを返し、一方でparse::<i32>Result<i32, ParseIntError>を返しています。

fn double_first(vec: Vec<&str>) -> i32 {
    let first = vec.first().unwrap(); // Generate error 1
                                      // エラー1の生成
    2 * first.parse::<i32>().unwrap() // Generate error 2
                                      // エラー2の生成
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    println!("The first doubled is {}", double_first(numbers));

    println!("The first doubled is {}", double_first(empty));
    // Error 1: the input vector is empty
    // エラー1:入力が空ベクトル

    println!("The first doubled is {}", double_first(strings));
    // Error 2: the element doesn't parse to a number
    // エラー2:要素が数字としてパースできない
}

この先の節では、これらの問題を処理する方法について見ていきます。

OptionからResultを取り出す

混在するエラー型に対する最も基本的な対処法は、単にお互いを埋め込んでしまうことです。

use std::num::ParseIntError;

fn double_first(vec: Vec<&str>) -> Option<Result<i32, ParseIntError>> {
    vec.first().map(|first| {
        first.parse::<i32>().map(|n| 2 * n)
    })
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    println!("The first doubled is {:?}", double_first(numbers));

    println!("The first doubled is {:?}", double_first(empty));
    // Error 1: the input vector is empty
    // エラー1:入力が空ベクトル

    println!("The first doubled is {:?}", double_first(strings));
    // Error 2: the element doesn't parse to a number
    // エラー2:要素が数字としてパースできない
}

中には、Optionの中身がNoneの場合はそのまま処理を進め、エラーの検出に限り実行を止めたいという場合もあるでしょう(?を使った時のように)。いくつかのコンビネータによって簡単にResultOptionをスワップすることができます。

use std::num::ParseIntError;

fn double_first(vec: Vec<&str>) -> Result<Option<i32>, ParseIntError> {
    let opt = vec.first().map(|first| {
        first.parse::<i32>().map(|n| 2 * n)
    });

    opt.map_or(Ok(None), |r| r.map(Some))
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    println!("The first doubled is {:?}", double_first(numbers));
    println!("The first doubled is {:?}", double_first(empty));
    println!("The first doubled is {:?}", double_first(strings));
}

エラー型を定義する

異なるエラー型をマスクし単一のエラー型として扱えるようにすると、コードがシンプルになる場合があります。ここでは自前のエラー型でそれを示してみます。

Rustはユーザーによる新たなエラー型の定義をサポートします。一般に「良い」エラー型は、

  • 異なるエラーをまとめて同じ型として扱う
  • ユーザーに優しいエラーメッセージを提供する
  • 他の型との比較を楽にする
    • 良い例:Err(EmptyVec)
    • 悪い例:Err("Please use a vector with at least one element".to_owned())
  • エラーについての情報を保持できる
    • 良い例:Err(BadChar(c, position))
    • 悪い例:Err("+ cannot be used here".to_owned())
  • 他のエラーと問題なく連携できる
use std::error;
use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

// Define our error types. These may be customized for our error handling cases.
// Now we will be able to write our own errors, defer to an underlying error
// implementation, or do something in between.
// 自前のエラー型の定義。エラーハンドリングのケースの応じてカスタマイズされる。
// ここで新たなエラーを書くことができ、元のエラーの実装に処理を委譲したり、
// その手前で何らかの処理を挟むことができます。
#[derive(Debug, Clone)]
struct DoubleError;

// Generation of an error is completely separate from how it is displayed.
// There's no need to be concerned about cluttering complex logic with the display style.
// エラーの生成は、それがどのように表示されるかとは別物です。
// そのため、エラーの表示スタイルに関する複雑なロジックを煩雑になるなどと気にする必要はありません。
//
// Note that we don't store any extra info about the errors. This means we can't state
// which string failed to parse without modifying our types to carry that information.
// エラーに関する余分な情報を持たせていないことに注意してください。
// どの文字列がパースに失敗したかなどを出力することは、
// その情報を保持させるようにエラーの定義を修正しない限りできません。
impl fmt::Display for DoubleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "invalid first item to double")
    }
}

// This is important for other errors to wrap this one.
impl error::Error for DoubleError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        // Generic error, underlying cause isn't tracked.
        // 基本となるエラー、原因は記録されていない。
        None
    }
}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first()
        // Change the error to our new type.
        // エラーを新たな型に変更する。
        .ok_or(DoubleError)
        .and_then(|s| {
            s.parse::<i32>()
                // Update to the new error type here also.
                // ここでも新たなエラー型に更新する。
                .map_err(|_| DoubleError)
                .map(|i| 2 * i)
        })
}

fn print(result: Result<i32>) {
    match result {
        Ok(n) => println!("The first doubled is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    print(double_first(numbers));
    print(double_first(empty));
    print(double_first(strings));
}

エラーをBoxする

元のエラーを維持しながらシンプルなコードを書くには、Boxしてしまうと良いでしょう。欠点として、元のエラー型はランタイムまで判明せず、静的に決定されないことが挙げられます。

標準ライブラリはBoxに、Fromを介してあらゆるErrorトレートを実装した型からBox<Error>トレートオブジェクトへの変換を実装させることで、エラーをboxしやすくしてくれます。

use std::error;
use std::fmt;

// Change the alias to `Box<error::Error>`.
// エイリアスを`Box<error::Error>`に変更する。
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug, Clone)]
struct EmptyVec;

impl fmt::Display for EmptyVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "invalid first item to double")
    }
}

impl error::Error for EmptyVec {}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first()
        .ok_or_else(|| EmptyVec.into()) // Converts to Box
                                        // Boxに変換
        .and_then(|s| {
            s.parse::<i32>()
                .map_err(|e| e.into()) // Converts to Box
                                       // Boxに変換
                .map(|i| 2 * i)
        })
}

fn print(result: Result<i32>) {
    match result {
        Ok(n) => println!("The first doubled is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    print(double_first(numbers));
    print(double_first(empty));
    print(double_first(strings));
}

参照

Dynamic dispatch and Error trait

?の他の活用法

以前の例ではparseの呼び出しに対するその場での対応として、エラーをライブラリのエラーからboxされたエラーへとmapしていました。

.and_then(|s| s.parse::<i32>()
    .map_err(|e| e.into())

簡単でよくあるオペレーションのため、可能なら省略してしまえると便利だったでしょう。でも残念、and_thenが十分にフレキシブルでないため、それはできません。ただその代わり、?なら使えます。

?の挙動は、unwrapまたはreturn Err(err)として説明されていました。これはほぼ正解で、本当はunwrap、もしくはreturn Err(From::from(err))という意味があります。From::fromは異なる型の間での変換ユーティリティであることから、エラーがリターン型に変換可能な場合に?を使うことで、その変換を自動的に行ってくれます。

前の例を?を使ったものに書き換えてみましょう。その結果、From::fromがエラー型に実装されている時map_errは消えてなくなります。

use std::error;
use std::fmt;

// Change the alias to `Box<dyn error::Error>`.
// エイリアスを`Box<error::Error>`に変更する。
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug)]
struct EmptyVec;

impl fmt::Display for EmptyVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "invalid first item to double")
    }
}

impl error::Error for EmptyVec {}

// The same structure as before but rather than chain all `Results`
// and `Options` along, we `?` to get the inner value out immediately.
// 前と同じ構造だが、`Results`と`Option`を繋げていく代わりに、
// `?`で内部の値をその場で取得します。
fn double_first(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first().ok_or(EmptyVec)?;
    let parsed = first.parse::<i32>()?;
    Ok(2 * parsed)
}

fn print(result: Result<i32>) {
    match result {
        Ok(n)  => println!("The first doubled is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    print(double_first(numbers));
    print(double_first(empty));
    print(double_first(strings));
}

これでかなり綺麗になりました。元のpanicと比べ、リターン型がResultであることを除けば、unwrapの呼び出しを?で置き換えたものに非常に似ています。結果、そのResultは上のレベルで分解されなければなりません。

参照

From::from and ?

エラーをラップする

Boxする方法の代替として、エラーを自前のエラー型としてラップする方法もあります。

use std::error;
use std::error::Error as _;
use std::num::ParseIntError;
use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

#[derive(Debug)]
enum DoubleError {
    EmptyVec,
    // We will defer to the parse error implementation for their error.
    // Supplying extra info requires adding more data to the type.
    // パースエラーの実装まで処理を委譲します。
    // 追加の情報を提供するには、型により多くのデータを追加する必要があります。
    Parse(ParseIntError),
}

impl fmt::Display for DoubleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DoubleError::EmptyVec =>
                write!(f, "please use a vector with at least one element"),
            // The wrapped error contains additional information and is available
            // via the source() method.
            // This is a wrapper, so defer to the underlying types' implementation of `fmt`.
            // ラップされたエラーは追加情報を含み、source メソッドから取り出すことができます。
            // これはラッパーなので、`fmt`での元となる型の実装に処理を任せます。
            DoubleError::Parse(..) =>
                write!(f, "the provided string could not be parsed as int"),
        }
    }
}

impl error::Error for DoubleError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            DoubleError::EmptyVec => None,
            // The cause is the underlying implementation error type. Is implicitly
            // cast to the trait object `&error::Error`. This works because the
            // underlying type already implements the `Error` trait.
            // 元の実装のエラー型が原因。
            // `&error::Error`トレートオブジェクトに暗にキャストされる。
            // 元となる型が`Error`トレートをすでに実装しているため問題なく動く。
            DoubleError::Parse(ref e) => Some(e),
        }
    }
}

// Implement the conversion from `ParseIntError` to `DoubleError`.
// This will be automatically called by `?` if a `ParseIntError`
// needs to be converted into a `DoubleError`.
// `ParseIntError`から`DoubleError`への変換の実装。
// `ParseIntError`が`DoubleError`に変換される必要がある時、自動的に`?`から呼び出される。
impl From<ParseIntError> for DoubleError {
    fn from(err: ParseIntError) -> DoubleError {
        DoubleError::Parse(err)
    }
}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first().ok_or(DoubleError::EmptyVec)?;
    // Here we implicitly use the `ParseIntError` implementation of `From` (which
    // we defined above) in order to create a `DoubleError`.
    let parsed = first.parse::<i32>()?;

    Ok(2 * parsed)
}

fn print(result: Result<i32>) {
    match result {
        Ok(n)  => println!("The first doubled is {}", n),
        Err(e) => {
            println!("Error: {}", e);
            if let Some(source) = e.source() {
                println!("  Caused by: {}", source);
            }
        },
    }
}

fn main() {
    let numbers = vec!["42", "93", "18"];
    let empty = vec![];
    let strings = vec!["tofu", "93", "18"];

    print(double_first(numbers));
    print(double_first(empty));
    print(double_first(strings));
}

これはエラーの処理のボイラープレートを増やしてしまい、全てのアプリケーションで必要になる訳では無いでしょう。これらのボイラープレートの処理を代わりにやってくれるようなライブラリもあります。

参照

From::from and Enums

Resultをイテレートする

Iter::mapオペレーションは失敗することもあります。例えば、

fn main() {
    let strings = vec!["tofu", "93", "18"];
    let numbers: Vec<_> = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Results: {:?}", numbers);
}

ここでは、この対処法についてみてみましょう。

filter_map()を使って失敗した要素のみを無視する

filter_mapは関数を呼び出し、結果がNoneになるものだけ取り除きます。

fn main() {
    let strings = vec!["tofu", "93", "18"];
    let numbers: Vec<_> = strings
        .into_iter()
        .filter_map(|s| s.parse::<i32>().ok())
        .collect();
    println!("Results: {:?}", numbers);
}

collect()で処理全体を失敗させる

Resultは、それらのベクトル(Vec<Result<T, E>>)からベクトルのそれ(Result<Vec<T>, E>)へと変換できるようにするため、FromIterを実装します。Result::Errが見つかり次第、イテレーションは終了します。

fn main() {
    let strings = vec!["tofu", "93", "18"];
    let numbers: Result<Vec<_>, _> = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Results: {:?}", numbers);
}

同じテクニックは、Optionを用いて行うこともできます。

partition()を使って全ての正常な値と失敗をまとめる

fn main() {
    let strings = vec!["tofu", "93", "18"];
    let (numbers, errors): (Vec<_>, Vec<_>) = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .partition(Result::is_ok);
    println!("Numbers: {:?}", numbers);
    println!("Errors: {:?}", errors);
}

結果を見てみると、まだ全てResultにラップされていることに気づくでしょう。もう少しのボイラープレートが必要です。

fn main() {
    let strings = vec!["tofu", "93", "18"];
    let (numbers, errors): (Vec<_>, Vec<_>) = strings
        .into_iter()
        .map(|s| s.parse::<i32>())
        .partition(Result::is_ok);
    let numbers: Vec<_> = numbers.into_iter().map(Result::unwrap).collect();
    let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect();
    println!("Numbers: {:?}", numbers);
    println!("Errors: {:?}", errors);
}

標準ライブラリの型

stdライブラリは、基本データ型を劇的に拡張するカスタム型を数多く提供します。例えば以下です。

  • 拡張可能な文字列であるString。例えば: "hello world"
  • オプション型: Option<i32>
  • エラーハンドリング用のResult<i32, i32>
  • ヒープ上資源のポインタBox<i32>

参照

基本データ型, stdライブラリ

Box, スタックとヒープ

Rustにおいて、すべての値はデフォルトでスタックに割り当てられます。Box<T>を作成することで、値を ボックス化 、すなわちヒープ上に割り当てることができます。ボックスとは正確にはヒープ上におかれたTの値へのスマートポインタです。ボックスがスコープを抜けると、デストラクタが呼ばれて内包するオブジェクトが破棄され、ヒープメモリが解放されます。

ボックス化された値は*オペレータを用いてデリファレンスすることができます。これにより一段と直接的な操作が可能になります。

use std::mem;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

// A Rectangle can be specified by where its top left and bottom right 
// corners are in space
#[allow(dead_code)]
struct Rectangle {
    top_left: Point,
    bottom_right: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> Box<Point> {
    // Allocate this point on the heap, and return a pointer to it
    // このPointをヒープ上に割り当て、ポインタを返す。
    Box::new(Point { x: 0.0, y: 0.0 })
}

fn main() {
    // (all the type annotations are superfluous)
    // Stack allocated variables
    // (以下では型を全て明示していますが、必須ではありません。)
    // この変数ははすべてスタック上に割り当てられる。
    let point: Point = origin();
    let rectangle: Rectangle = Rectangle {
        top_left: origin(),
        bottom_right: Point { x: 3.0, y: -4.0 }
    };

    // Heap allocated rectangle
    // ヒープ上に割り当てられたRectangle
    let boxed_rectangle: Box<Rectangle> = Box::new(Rectangle {
        top_left: origin(),
        bottom_right: Point { x: 3.0, y: -4.0 },
    });

    // The output of functions can be boxed
    // 関数の返り値をボックス化
    let boxed_point: Box<Point> = Box::new(origin());

    // Double indirection
    // 間にもう一つポインタを挟む
    let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());

    println!("Point occupies {} bytes on the stack",
             mem::size_of_val(&point));
    println!("Rectangle occupies {} bytes on the stack",
             mem::size_of_val(&rectangle));

    // box size == pointer size
    // ボックスのサイズはポインタのサイズに等しい
    println!("Boxed point occupies {} bytes on the stack",
             mem::size_of_val(&boxed_point));
    println!("Boxed rectangle occupies {} bytes on the stack",
             mem::size_of_val(&boxed_rectangle));
    println!("Boxed box occupies {} bytes on the stack",
             mem::size_of_val(&box_in_a_box));

    // Copy the data contained in `boxed_point` into `unboxed_point`
    // `boxed_point`の保持するデータを`unboxed_point`にコピーする
    let unboxed_point: Point = *boxed_point;
    println!("Unboxed point occupies {} bytes on the stack",
             mem::size_of_val(&unboxed_point));
}

ベクタ型

「ベクタ」はサイズを変更可能な配列です。スライスと同様、そのサイズはコンパイル時には不定ですが、いつでも要素を追加したり削除したりすることができます。ベクタは3つの要素で、その特徴が完全に決まります。

  • データへのポインタ
  • 長さ
  • 容量 ... あらかじめメモリ上にベクタのために確保された領域

ベクタはその容量を超えない限りにおいて長くしていくことができます。超えた場合には、より大きな容量を持つように割り当てなおされます。

fn main() {
    // Iterators can be collected into vectors
    // イテレータは要素を収集してベクタにすることができる。
    let collected_iterator: Vec<i32> = (0..10).collect();
    println!("Collected (0..10) into: {:?}", collected_iterator);

    // The `vec!` macro can be used to initialize a vector
    // ベクタの初期化には`vec!`マクロが使用できる。
    let mut xs = vec![1i32, 2, 3];
    println!("Initial vector: {:?}", xs);

    // Insert new element at the end of the vector
    // 新しい要素をベクタの最後に挿入することができる。
    println!("Push 4 into the vector");
    xs.push(4);
    println!("Vector: {:?}", xs);

    // Error! Immutable vectors can't grow
    // エラー!イミュータブルなベクタは成長できない
    collected_iterator.push(0);
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう。

    // The `len` method yields the number of elements currently stored in a vector
    // `len`メソッドは現在のベクタのサイズを返す。
    println!("Vector length: {}", xs.len());

    // Indexing is done using the square brackets (indexing starts at 0)
    // 鍵括弧(`[]`)を用いてインデックスによる要素へのアクセスができる
    // (インデックスは0から開始する)
    println!("Second element: {}", xs[1]);

    // `pop` removes the last element from the vector and returns it
    // `pop`はベクタの最後の要素を削除すると同時に返す。
    println!("Pop last element: {:?}", xs.pop());

    // Out of bounds indexing yields a panic
    // 不正なインデックスアクセスはpanicを引き起こします。
    println!("Fourth element: {}", xs[3]);
    // FIXME ^ Comment out this line

    // `Vector`s can be easily iterated over
    println!("Contents of xs:");
    for x in xs.iter() {
        println!("> {}", x);
    }

    // A `Vector` can also be iterated over while the iteration
    // count is enumerated in a separate variable (`i`)
    for (i, x) in xs.iter().enumerate() {
        println!("In position {} we have value {}", i, x);
    }

    // Thanks to `iter_mut`, mutable `Vector`s can also be iterated
    // over in a way that allows modifying each value
    for x in xs.iter_mut() {
        *x *= 3;
    }
    println!("Updated vector: {:?}", xs);
}

Vec型のメソッドの一覧はstd::vecモジュールを見てください。

文字列

Rustには文字列を扱う型が2つあります。String&strです。

Stringは有効なUTF-8の配列であることを保証されたバイトのベクタ(Vec<u8>)として保持されます。ヒープ上に保持され、伸長可能で、末端にnull文字を含みません。

&strは有効なUTF-8の配列のスライス(&[u8])で、いつでもStringに変換することができます。&[T]がいつでもVec<T>に変換できるのと同様です。

fn main() {
    // (all the type annotations are superfluous)
    // A reference to a string allocated in read only memory
    // (以下の例では型を明示していますが、これらは必須ではありません。)
    // read only memory上に割り当てられた文字列への参照
    let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
    println!("Pangram: {}", pangram);

    // Iterate over words in reverse, no new string is allocated
    // 単語を逆順にイテレートする。新しい文字列の割り当ては起こらない。
    println!("Words in reverse");
    for word in pangram.split_whitespace().rev() {
        println!("> {}", word);
    }

    // Copy chars into a vector, sort and remove duplicates
    // 文字をベクトルにコピー。ソートして重複を除去
    let mut chars: Vec<char> = pangram.chars().collect();
    chars.sort();
    chars.dedup();

    // Create an empty and growable `String`
    // 中身が空で、伸長可能な`String`を作成
    let mut string = String::new();
    for c in chars {
        // Insert a char at the end of string
        // 文字を文字列の末端に挿入
        string.push(c);
        // Insert a string at the end of string
        // 文字列を文字列の末端に挿入
        string.push_str(", ");
    }

    // The trimmed string is a slice to the original string, hence no new
    // allocation is performed
    // 文字列のトリミング(特定文字種の除去)はオリジナルの文字列のスライスを
    // 返すので、新規のメモリ割り当ては発生しない。
    let chars_to_trim: &[char] = &[' ', ','];
    let trimmed_str: &str = string.trim_matches(chars_to_trim);
    println!("Used characters: {}", trimmed_str);

    // Heap allocate a string
    // 文字列をヒープに割り当てる。
    let alice = String::from("I like dogs");
    // Allocate new memory and store the modified string there
    // 新しくメモリを確保し、変更を加えた文字列をそこに割り当てる。
    let bob: String = alice.replace("dog", "cat");

    println!("Alice says: {}", alice);
    println!("Bob says: {}", bob);
}

str/Stringのメソッドをもっと見たい場合はstd::strstd::stringモジュールを参照してください。

Literals and escapes

There are multiple ways to write string literals with special characters in them. All result in a similar &str so it's best to use the form that is the most convenient to write. Similarly there are multiple ways to write byte string literals, which all result in &[u8; N].

Generally special characters are escaped with a backslash character: \. This way you can add any character to your string, even unprintable ones and ones that you don't know how to type. If you want a literal backslash, escape it with another one: \\

String or character literal delimiters occuring within a literal must be escaped: "\"", '\''.

fn main() {
    // You can use escapes to write bytes by their hexadecimal values...
    let byte_escape = "I'm writing \x52\x75\x73\x74!";
    println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);

    // ...or Unicode code points.
    let unicode_codepoint = "\u{211D}";
    let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";

    println!("Unicode character {} (U+211D) is called {}",
                unicode_codepoint, character_name );


    let long_string = "String literals
                        can span multiple lines.
                        The linebreak and indentation here ->\
                        <- can be escaped too!";
    println!("{}", long_string);
}

Sometimes there are just too many characters that need to be escaped or it's just much more convenient to write a string out as-is. This is where raw string literals come into play.

fn main() {
    let raw_str = r"Escapes don't work here: \x3F \u{211D}";
    println!("{}", raw_str);

    // If you need quotes in a raw string, add a pair of #s
    let quotes = r#"And then I said: "There is no escape!""#;
    println!("{}", quotes);

    // If you need "# in your string, just use more #s in the delimiter.
    // There is no limit for the number of #s you can use.
    let longer_delimiter = r###"A string with "# in it. And even "##!"###;
    println!("{}", longer_delimiter);
}

Want a string that's not UTF-8? (Remember, str and String must be valid UTF-8). Or maybe you want an array of bytes that's mostly text? Byte strings to the rescue!

use std::str;

fn main() {
    // Note that this is not actually a `&str`
    let bytestring: &[u8; 21] = b"this is a byte string";

    // Byte arrays don't have the `Display` trait, so printing them is a bit limited
    println!("A byte string: {:?}", bytestring);

    // Byte strings can have byte escapes...
    let escaped = b"\x52\x75\x73\x74 as bytes";
    // ...but no unicode escapes
    // let escaped = b"\u{211D} is not allowed";
    println!("Some escaped bytes: {:?}", escaped);


    // Raw byte strings work just like raw strings
    let raw_bytestring = br"\u{211D} is not escaped here";
    println!("{:?}", raw_bytestring);

    // Converting a byte array to `str` can fail
    if let Ok(my_str) = str::from_utf8(raw_bytestring) {
        println!("And the same as text: '{}'", my_str);
    }

    let _quotes = br#"You can also use "fancier" formatting, \
                    like with normal raw strings"#;

    // Byte strings don't have to be UTF-8
    let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" in SHIFT-JIS

    // But then they can't always be converted to `str`
    match str::from_utf8(shift_jis) {
        Ok(my_str) => println!("Conversion successful: '{}'", my_str),
        Err(e) => println!("Conversion failed: {:?}", e),
    };
}

For conversions between character encodings check out the encoding crate.

A more detailed listing of the ways to write string literals and escape characters is given in the 'Tokens' chapter of the Rust Reference.

Option

プログラムの一部が失敗した際、panic!するよりも、エラーを補足する方が望ましい場合があります。これはOptionという列挙型を用いることで可能になります。

列挙型Option<T>には2つの値があります。

  • None、これは実行の失敗か値の欠如を示します。
  • Some(value)、型Tvalueをラップするタプルです。
// An integer division that doesn't `panic!`
// `panic!`を起こさない整数の割り算
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
    if divisor == 0 {
        // Failure is represented as the `None` variant
        // 失敗は`None`としてあらわされる。
        None
    } else {
        // Result is wrapped in a `Some` variant
        // 結果は`Some`にラップされる。
        Some(dividend / divisor)
    }
}

// This function handles a division that may not succeed
// この関数は失敗する割り算を扱うことができる
fn try_division(dividend: i32, divisor: i32) {
    // `Option` values can be pattern matched, just like other enums
    // `Option` の値は、他のあらゆる列挙型と同様パターンマッチに使用できる。
    match checked_division(dividend, divisor) {
        None => println!("{} / {} failed!", dividend, divisor),
        Some(quotient) => {
            println!("{} / {} = {}", dividend, divisor, quotient)
        },
    }
}

fn main() {
    try_division(4, 2);
    try_division(1, 0);

    // Binding `None` to a variable needs to be type annotated
    // `None`を変数にアサインする際は、型を明示しなくてはならない。
    let none: Option<i32> = None;
    let _equivalent_none = None::<i32>;

    let optional_float = Some(0f32);

    // Unwrapping a `Some` variant will extract the value wrapped.
    // `Some`をアンラップすると中の値を取得できる。
    println!("{:?} unwraps to {:?}", optional_float, optional_float.unwrap());

    // Unwrapping a `None` variant will `panic!`
    // `None`をアンラップしようとすると`panic!`る
    println!("{:?} unwraps to {:?}", none, none.unwrap());
}

Result

これまでの例で、失敗する可能性のある関数の返り値として、列挙型Optionが使用でき、失敗時の返り値にはNoneを用いることを見てきました。しかし、時には なぜ そのオペレーションが失敗したのかを明示することが重要な場合があります。そのためにはResult列挙型を使用します。

列挙型Result<T, E>は2つの値をとりえます。

  • Ok(value) ... これはオペレーションが成功したことを意味し、返り値valueをラップします。(valueは型Tを持ちます。)
  • Err(why) ... これはオペレーションの失敗を意味します。whyをラップしており、ここには失敗した理由が(必ずではありませんが)書かれています。(whyの型はEです。)
mod checked {
    // Mathematical "errors" we want to catch
    // 補足対象としたい、数学的な「エラー」
    #[derive(Debug)]
    pub enum MathError {
        DivisionByZero,
        NonPositiveLogarithm,
        NegativeSquareRoot,
    }

    pub type MathResult = Result<f64, MathError>;

    pub fn div(x: f64, y: f64) -> MathResult {
        if y == 0.0 {
            // This operation would `fail`, instead let's return the reason of
            // the failure wrapped in `Err`
            // 分母が0なので、このオペレーションは普通に行えば失敗する。
            // 代わりに`Err`でラップされた失敗の理由を返そう。
            Err(MathError::DivisionByZero)
        } else {
            // This operation is valid, return the result wrapped in `Ok`
            // このオペレーションは問題がないので、結果を`Ok`でラップして返そう。
            Ok(x / y)
        }
    }

    pub fn sqrt(x: f64) -> MathResult {
        if x < 0.0 {
            Err(MathError::NegativeSquareRoot)
        } else {
            Ok(x.sqrt())
        }
    }

    pub fn ln(x: f64) -> MathResult {
        if x <= 0.0 {
            Err(MathError::NonPositiveLogarithm)
        } else {
            Ok(x.ln())
        }
    }
}

// `op(x, y)` === `sqrt(ln(x / y))`
fn op(x: f64, y: f64) -> f64 {
    // This is a three level match pyramid!
    // 3段階の`match`ピラミッド!
    match checked::div(x, y) {
        Err(why) => panic!("{:?}", why),
        Ok(ratio) => match checked::ln(ratio) {
            Err(why) => panic!("{:?}", why),
            Ok(ln) => match checked::sqrt(ln) {
                Err(why) => panic!("{:?}", why),
                Ok(sqrt) => sqrt,
            },
        },
    }
}

fn main() {
    // Will this fail?
    // これは失敗するだろうか?
    println!("{}", op(1.0, 10.0));
}

?

Chaining results using match can get pretty untidy; luckily, the ? operator can be used to make things pretty again. ? is used at the end of an expression returning a Result, and is equivalent to a match expression, where the Err(err) branch expands to an early Err(From::from(err)), and the Ok(ok) branch expands to an ok expression.

mod checked {
    #[derive(Debug)]
    enum MathError {
        DivisionByZero,
        NonPositiveLogarithm,
        NegativeSquareRoot,
    }

    type MathResult = Result<f64, MathError>;

    fn div(x: f64, y: f64) -> MathResult {
        if y == 0.0 {
            Err(MathError::DivisionByZero)
        } else {
            Ok(x / y)
        }
    }

    fn sqrt(x: f64) -> MathResult {
        if x < 0.0 {
            Err(MathError::NegativeSquareRoot)
        } else {
            Ok(x.sqrt())
        }
    }

    fn ln(x: f64) -> MathResult {
        if x <= 0.0 {
            Err(MathError::NonPositiveLogarithm)
        } else {
            Ok(x.ln())
        }
    }

    // Intermediate function
    fn op_(x: f64, y: f64) -> MathResult {
        // if `div` "fails", then `DivisionByZero` will be `return`ed
        let ratio = div(x, y)?;

        // if `ln` "fails", then `NonPositiveLogarithm` will be `return`ed
        let ln = ln(ratio)?;

        sqrt(ln)
    }

    pub fn op(x: f64, y: f64) {
        match op_(x, y) {
            Err(why) => panic!("{}", match why {
                MathError::NonPositiveLogarithm
                    => "logarithm of non-positive number",
                MathError::DivisionByZero
                    => "division by zero",
                MathError::NegativeSquareRoot
                    => "square root of negative number",
            }),
            Ok(value) => println!("{}", value),
        }
    }
}

fn main() {
    checked::op(1.0, 10.0);
}

Be sure to check the documentation, as there are many methods to map/compose Result.

panic!

panic!マクロはパニックを生成し、スタックの巻き戻しを開始するために使用することができます。巻き戻しの間、ランタイムは、(訳注: panicを起こした)スレッドが 所有権を持つ 全ての資源のデストラクタを呼び出し、メモリ上から解放します。

今回はシングルスレッドのプログラムを実行しているので、panic!はプログラムにパニックメッセージを表示させ、exitします。

// Re-implementation of integer division (/)
// 整数の除法(/)の再実装
fn division(dividend: i32, divisor: i32) -> i32 {
    if divisor == 0 {
        // Division by zero triggers a panic
        // ゼロによる除算はパニックを引き起こす
        panic!("division by zero");
    } else {
        dividend / divisor
    }
}

// The `main` task
// `main`のタスク
fn main() {
    // Heap allocated integer
    // ヒープ上の整数
    let _x = Box::new(0i32);

    // This operation will trigger a task failure
    // このオペレーションはタスクの失敗を引き起こす
    division(3, 0);

    println!("This point won't be reached!");

    // `_x` should get destroyed at this point
    // `_x`はここに到達する前に破棄される。
}

panic!がメモリリークを引き起こさないことを確認しましょう。

$ rustc panic.rs && valgrind ./panic
==4401== Memcheck, a memory error detector
==4401== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4401== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==4401== Command: ./panic
==4401== 
thread '<main>' panicked at 'division by zero', panic.rs:5
==4401== 
==4401== HEAP SUMMARY:
==4401==     in use at exit: 0 bytes in 0 blocks
==4401==   total heap usage: 18 allocs, 18 frees, 1,648 bytes allocated
==4401== 
==4401== All heap blocks were freed -- no leaks are possible
==4401== 
==4401== For counts of detected and suppressed errors, rerun with: -v
==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

ハッシュマップ

ベクタ型が値を整数のインデックスで保持するのに対し、HashMapではキーで保持します。HashMapのキーはブーリアン、整数、文字列等のEqあるいはHashトレイトを保持する型なら何でもOKです。後でより詳しく見ていきます。

ベクタ型と同様、伸長可能ですが、HashMapの場合さらに、スペースが余っているときには小さくすることも可能です。HashMapを一定の容量のエリアに作成するときはHashMap::with_capacity(uint)を、デフォルトの容量で作成するときはHashMap::new()を用います。後者が推奨されています。

use std::collections::HashMap;

fn call(number: &str) -> &str {
    match number {
        "798-1364" => "We're sorry, the call cannot be completed as dialed. 
            Please hang up and try again.",
        "645-7689" => "Hello, this is Mr. Awesome's Pizza. My name is Fred.
            What can I get for you today?",
        _ => "Hi! Who is this again?"
    }
}

fn main() { 
    let mut contacts = HashMap::new();

    contacts.insert("Daniel", "798-1364");
    contacts.insert("Ashley", "645-7689");
    contacts.insert("Katie", "435-8291");
    contacts.insert("Robert", "956-1745");

    // Takes a reference and returns Option<&V>
    // 参照をとり、Option<&V>を返す。
    match contacts.get(&"Daniel") {
        Some(&number) => println!("Calling Daniel: {}", call(number)),
        _ => println!("Don't have Daniel's number."),
    }

    // `HashMap::insert()` returns `None`
    // if the inserted value is new, `Some(value)` otherwise
    // `HashMap::insert()`は
    // insertされた値が新しい場合は`None`を
    // そうでなければ`Some(value)`を返す。
    contacts.insert("Daniel", "164-6743");

    match contacts.get(&"Ashley") {
        Some(&number) => println!("Calling Ashley: {}", call(number)),
        _ => println!("Don't have Ashley's number."),
    }

    contacts.remove(&"Ashley"); 

    // `HashMap::iter()` returns an iterator that yields 
    // (&'a key, &'a value) pairs in arbitrary order.
    // `HashMap::iter()`は(&'a key, &'a value)
    // のペアを順不同で産出するイテレータを返す
    for (contact, &number) in contacts.iter() {
        println!("Calling {}: {}", contact, call(number)); 
    }
}

ハッシングやハッシュマップ(ハッシュテーブルと呼ばれることもあります)の仕組みについて、より詳しく知りたい場合はWikipediaのハッシュテーブルのページを見てください。

key型の変種

EqHashトレイトを実装している型ならば、なんでもHashMapのキーになることができます。例えば以下です。

  • bool (キーになりうる値が2つしかないので実用的ではないですが…)
  • intuint、あるいは他の整数型
  • String&str(Tips: Stringをキーにしたハッシュマップを作製した場合、.get()メソッドの引数に&strを与えて値を取得することができます。)

f32f64Hashを実装して いない ことに注意しましょう。おそらくこれは浮動小数点演算時に誤差が発生するため、キーとして使用すると、恐ろしいほどエラーの元となるためです。

集合型は、その要素となっている全ての型がEqを、あるいはHashを実装している場合、必ず同じトレイトを実装しています。例えば、Vec<T>THashを実装している場合、Hashを実装します。

独自の型にEqあるいはHashを実装するのは簡単です。以下の一行で済みます。 #[derive(PartialEq, Eq, Hash)]

後はコンパイラがよしなにしてくれます。これらのトレイトの詳細をコントロールしたい場合、EqHashを自分で実装することもできます。この文書ではHashトレイトを実装する方法の詳細については触れません。

structHashMapで扱う際の例として、とてもシンプルなユーザーログインシステムを作成してみましょう。

use std::collections::HashMap;

// Eq requires that you derive PartialEq on the type.
// Eqトレイトを使用する時は、PartialEqをderiveする必要があります。
#[derive(PartialEq, Eq, Hash)]
struct Account<'a>{
    username: &'a str,
    password: &'a str,
}

struct AccountInfo<'a>{
    name: &'a str,
    email: &'a str,
}

type Accounts<'a> = HashMap<Account<'a>, AccountInfo<'a>>;

fn try_logon<'a>(accounts: &Accounts<'a>,
        username: &'a str, password: &'a str){
    println!("Username: {}", username);
    println!("Password: {}", password);
    println!("Attempting logon...");

    let logon = Account {
        username,
        password,
    };

    match accounts.get(&logon) {
        Some(account_info) => {
            println!("Successful logon!");
            println!("Name: {}", account_info.name);
            println!("Email: {}", account_info.email);
        },
        _ => println!("Login failed!"),
    }
}

fn main(){
    let mut accounts: Accounts = HashMap::new();

    let account = Account {
        username: "j.everyman",
        password: "password123",
    };

    let account_info = AccountInfo {
        name: "John Everyman",
        email: "j.everyman@email.com",
    };

    accounts.insert(account, account_info);

    try_logon(&accounts, "j.everyman", "psasword123");

    try_logon(&accounts, "j.everyman", "password123");
}

ハッシュ集合

値がなく、キーだけのHashMapを想像してみてください。これはハッシュ集合(HashSet)と呼ばれるものです。(HashSet<T>は、実際にはHashMap<T, ()>のラッパーです。)

「何の意味があるの?フツーにキーをVecに入れればいいじゃん」そう思いましたね?

それは、HashSet独自の機能として、要素に重複がないということが保証されるためです。これは全ての集合(set)型がもつ機能です。HashSetはその実装の1つであり、他にはBTreeSet等があります。

HashSetに、すでに存在する値を加えようとすると、(すなわち、加えようとしている値のハッシュ値と、要素中のいずれかの値のハッシュ値が等しい場合、)新しい値によって古い値が上書きされます。

これは、同じ値を2つ以上欲しくない場合や、すでにある値を持っているか知りたい場合にとても有効です。

しかし、集合型の機能はそれだけではありません。

集合型には4つの主要なメソッドがあり、(すべてイテレータを返します。)

  • union: 2つの集合型のどちらか一方にある値を全て取得
  • difference: 1つ目の集合にあり、かつ2つ目には存在しない値を全て取得。
  • intersection: 両方の集合にある値のみを取得。
  • symmetric_difference: どちらか一方の集合には存在するが、両方には ない 値を取得

以下の例でこれらをすべて見ていきましょう。

use std::collections::HashSet;

fn main() {
    let mut a: HashSet<i32> = vec![1i32, 2, 3].into_iter().collect();
    let mut b: HashSet<i32> = vec![2i32, 3, 4].into_iter().collect();

    assert!(a.insert(4));
    assert!(a.contains(&4));

    // `HashSet::insert()` returns false if
    // there was a value already present.
    // 既に存在する値を追加しようとすると
    // `HashSet::insert()`はfalseを返す。
    assert!(b.insert(4), "Value 4 is already in set B!");
    // FIXME ^ Comment out this line
    // FIXME ^ この行をコメントアウトしましょう。

    b.insert(5);

    // If a collection's element type implements `Debug`,
    // then the collection implements `Debug`.
    // It usually prints its elements in the format `[elem1, elem2, ...]`
    // 集合の要素が、`Debug`を実装している型の場合、
    // 集合そのものも`Debug`を実装する。
    // 通常は`[elem1, elem2, ...]`のように要素をプリントする。
    println!("A: {:?}", a);
    println!("B: {:?}", b);

    // Print [1, 2, 3, 4, 5] in arbitrary order
    // [1, 2, 3, 4, 5]を順不同にプリント
    println!("Union: {:?}", a.union(&b).collect::<Vec<&i32>>());

    // This should print [1]
    // これは[1]をプリント
    println!("Difference: {:?}", a.difference(&b).collect::<Vec<&i32>>());

    // Print [2, 3, 4] in arbitrary order.
    // [2, 3, 4]を順不同にプリント
    println!("Intersection: {:?}", a.intersection(&b).collect::<Vec<&i32>>());

    // Print [1, 5]
    // [1, 5]をプリント
    println!("Symmetric Difference: {:?}",
             a.symmetric_difference(&b).collect::<Vec<&i32>>());
}

例は公式ドキュメントから持ってきています。

Rc

When multiple ownership is needed, Rc(Reference Counting) can be used. Rc keeps track of the number of the references which means the number of owners of the value wrapped inside an Rc.

Reference count of an Rc increases by 1 whenever an Rc is cloned, and decreases by 1 whenever one cloned Rc is dropped out of the scope. When an Rc's reference count becomes zero, which means there are no owners remained, both the Rc and the value are all dropped.

Cloning an Rc never performs a deep copy. Cloning creates just another pointer to the wrapped value, and increments the count.

use std::rc::Rc;

fn main() {
    let rc_examples = "Rc examples".to_string();
    {
        println!("--- rc_a is created ---");
        
        let rc_a: Rc<String> = Rc::new(rc_examples);
        println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
        
        {
            println!("--- rc_a is cloned to rc_b ---");
            
            let rc_b: Rc<String> = Rc::clone(&rc_a);
            println!("Reference Count of rc_b: {}", Rc::strong_count(&rc_b));
            println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
            
            // Two `Rc`s are equal if their inner values are equal
            println!("rc_a and rc_b are equal: {}", rc_a.eq(&rc_b));
            
            // We can use methods of a value directly
            println!("Length of the value inside rc_a: {}", rc_a.len());
            println!("Value of rc_b: {}", rc_b);
            
            println!("--- rc_b is dropped out of scope ---");
        }
        
        println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
        
        println!("--- rc_a is dropped out of scope ---");
    }
    
    // Error! `rc_examples` already moved into `rc_a`
    // And when `rc_a` is dropped, `rc_examples` is dropped together
    // println!("rc_examples: {}", rc_examples);
    // TODO ^ Try uncommenting this line
}

参照

std::rc and std::sync::arc.

Arc

When shared ownership between threads is needed, Arc(Atomic Reference Counted) can be used. This struct, via the Clone implementation can create a reference pointer for the location of a value in the memory heap while increasing the reference counter. As it shares ownership between threads, when the last reference pointer to a value is out of scope, the variable is dropped.

use std::sync::Arc;
use std::thread;

fn main() {
    // This variable declaration is where its value is specified.
    let apple = Arc::new("the same apple");

    for _ in 0..10 {
        // Here there is no value specification as it is a pointer to a reference
        // in the memory heap.
        let apple = Arc::clone(&apple);

        thread::spawn(move || {
            // As Arc was used, threads can be spawned using the value allocated
            // in the Arc variable pointer's location.
            println!("{:?}", apple);
        });
    }
}

標準ライブラリのその他

他にも、様々な型がstdライブラリの中で提供されています。例えば以下の機能を果たすための物があります。

  • スレッド
  • チャネル
  • ファイルI/O

これらにより基本データ型の提供する機能よりも遥かに豊かなことが実現できます。

参照

基本データ型, stdライブラリ

スレッド

Rustはspawn関数を用いてOSのネイティブスレッドを開始することができます。この関数の引数はmoveクロージャ(訳注: 参照ではなく値を取るクロージャ。 詳しくは[クロージャを返す関数][fn_output]を参照)です。

use std::thread;

const NTHREADS: u32 = 10;

// This is the `main` thread
// この関数は`main`スレッドで実行される。
fn main() {
    // Make a vector to hold the children which are spawned.
    // spawnされるクロージャを保持するためのベクタ
    let mut children = vec![];

    for i in 0..NTHREADS {
        // Spin up another thread
        // 新しいスレッドを起動
        children.push(thread::spawn(move || {
            println!("this is thread number {}", i);
        }));
    }

    for child in children {
        // Wait for the thread to finish. Returns a result.
        // 子スレッドが終了するのを待ち、結果を返す。
        let _ = child.join();
    }
}

これらのスレッドのスケジューリングはOSによって行われる。

Testcase: map-reduce

Rust makes it very easy to parallelise data processing, without many of the headaches traditionally associated with such an attempt.

The standard library provides great threading primitives out of the box. These, combined with Rust's concept of Ownership and aliasing rules, automatically prevent data races.

The aliasing rules (one writable reference XOR many readable references) automatically prevent you from manipulating state that is visible to other threads. (Where synchronisation is needed, there are synchronisation primitives like Mutexes or Channels.)

In this example, we will calculate the sum of all digits in a block of numbers. We will do this by parcelling out chunks of the block into different threads. Each thread will sum its tiny block of digits, and subsequently we will sum the intermediate sums produced by each thread.

Note that, although we're passing references across thread boundaries, Rust understands that we're only passing read-only references, and that thus no unsafety or data races can occur. Because we're move-ing the data segments into the thread, Rust will also ensure the data is kept alive until the threads exit, so no dangling pointers occur.

use std::thread;

// This is the `main` thread
fn main() {

    // This is our data to process.
    // We will calculate the sum of all digits via a threaded  map-reduce algorithm.
    // Each whitespace separated chunk will be handled in a different thread.
    //
    // TODO: see what happens to the output if you insert spaces!
    let data = "86967897737416471853297327050364959
11861322575564723963297542624962850
70856234701860851907960690014725639
38397966707106094172783238747669219
52380795257888236525459303330302837
58495327135744041048897885734297812
69920216438980873548808413720956532
16278424637452589860345374828574668";

    // Make a vector to hold the child-threads which we will spawn.
    let mut children = vec![];

    /*************************************************************************
     * "Map" phase
     *
     * Divide our data into segments, and apply initial processing
     ************************************************************************/

    // split our data into segments for individual calculation
    // each chunk will be a reference (&str) into the actual data
    let chunked_data = data.split_whitespace();

    // Iterate over the data segments.
    // .enumerate() adds the current loop index to whatever is iterated
    // the resulting tuple "(index, element)" is then immediately
    // "destructured" into two variables, "i" and "data_segment" with a
    // "destructuring assignment"
    for (i, data_segment) in chunked_data.enumerate() {
        println!("data segment {} is \"{}\"", i, data_segment);

        // Process each data segment in a separate thread
        //
        // spawn() returns a handle to the new thread,
        // which we MUST keep to access the returned value
        //
        // 'move || -> u32' is syntax for a closure that:
        // * takes no arguments ('||')
        // * takes ownership of its captured variables ('move') and
        // * returns an unsigned 32-bit integer ('-> u32')
        //
        // Rust is smart enough to infer the '-> u32' from
        // the closure itself so we could have left that out.
        //
        // TODO: try removing the 'move' and see what happens
        children.push(thread::spawn(move || -> u32 {
            // Calculate the intermediate sum of this segment:
            let result = data_segment
                        // iterate over the characters of our segment..
                        .chars()
                        // .. convert text-characters to their number value..
                        .map(|c| c.to_digit(10).expect("should be a digit"))
                        // .. and sum the resulting iterator of numbers
                        .sum();

            // println! locks stdout, so no text-interleaving occurs
            println!("processed segment {}, result={}", i, result);

            // "return" not needed, because Rust is an "expression language", the
            // last evaluated expression in each block is automatically its value.
            result

        }));
    }


    /*************************************************************************
     * "Reduce" phase
     *
     * Collect our intermediate results, and combine them into a final result
     ************************************************************************/

    // collect each thread's intermediate results into a new Vec
        // collect each child thread's return-value
    // combine each thread's intermediate results into a single final sum.
    //
    // we use the "turbofish" ::<> to provide sum() with a type hint.
    //
    // TODO: try without the turbofish, by instead explicitly
    // specifying the type of final_result
    let final_result = children.into_iter().map(|c| c.join().unwrap()).sum::<u32>();

    println!("Final sum result: {}", final_result);
}


Assignments

It is not wise to let our number of threads depend on user inputted data. What if the user decides to insert a lot of spaces? Do we really want to spawn 2,000 threads? Modify the program so that the data is always chunked into a limited number of chunks, defined by a static constant at the beginning of the program.

参照

チャネル

Rustは、スレッド間のコミュニケーションのために、非同期のチャネル(channels)を提供しています。チャネル2つのエンドポイント、すなわち送信者(Sender)と受信者(Receiver)を介して、情報の一方向への流れを作り出すことを可能にしています。

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

static NTHREADS: i32 = 3;

fn main() {
    // Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`,
    // where `T` is the type of the message to be transferred
    // (type annotation is superfluous)
    // チャネルには`Sender<T>`と`Receiver<T>`という2つのエンドポイントがある。
    // ここで、`T`は送信されるメッセージの型である。
    // (型アノテーションは必須ではない。)
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
    let mut children = Vec::new();

    for id in 0..NTHREADS {
        // The sender endpoint can be copied
        // 送信者エンドポイントはコピーすることができる。
        let thread_tx = tx.clone();

        // Each thread will send its id via the channel
        // ここでは、それぞれのスレッドが自身のIDを送信している。
        let child = thread::spawn(move || {
            // The thread takes ownership over `thread_tx`
            // Each thread queues a message in the channel
            // スレッドは`thread_tx`の所有権をとり、それぞれのスレッドは
            // メッセージをチャネルにキューイングする。
            thread_tx.send(id).unwrap();

            // Sending is a non-blocking operation, the thread will continue
            // immediately after sending its message
            // 送信はノンブロッキングなオペレーションなので、
            // メッセージを送信した後もすぐに実行を継続する。
            println!("thread {} finished", id);
        });

        children.push(child);
    }

    // Here, all the messages are collected
    // ここで、全てのメッセージが収集される。
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // The `recv` method picks a message from the channel
        // `recv` will block the current thread if there are no messages available
        // `recv`メソッドはチャネルからメッセージを取り出す。
        // もし取り出せるメッセージが存在しない場合、`recv`は
        // 現在のスレッドをブロックする。
        ids.push(rx.recv());
    }
    
    // Wait for the threads to complete any remaining work
    for child in children {
        child.join().expect("oops! the child thread panicked");
    }

    // Show the order in which the messages were sent
    // メッセージが送信された順番を表示
    println!("{:?}", ids);
}

Path

構造体Pathは、ファイルシステム中のパスを表します。Pathには2つの変種があります。UNIXライクなファイルシステムのためのposix::Pathと、Windows用のwindows::Pathです。それぞれプラットフォームに対応したPathをエクスポートします。

PathOsStrから作ることができます。そうすればそのパスが指すファイル・ディレクトリの情報を取得するためのメソッドがいくつか使えるようになります。

Pathの実態はUTF-8の文字列 ではなく 、バイト型のベクタ(Vec<u8>)であることに注意しましょう。したがって、Path&strに変換するのは無条件 ではなく 、失敗する可能性があります。それゆえOption型が返されます。

use std::path::Path;

fn main() {
    // Create a `Path` from an `&'static str`
    // `&'static str`から`Path`を作成
    let path = Path::new(".");

    // The `display` method returns a `Display`able structure
    // `display`メソッドは`Display`可能な構造体を返す。
    let _display = path.display();

    // `join` merges a path with a byte container using the OS specific
    // separator, and returns the new path
    // `join`はOS固有のセパレータによってバイトのコンテナ型であるパス
    // を結合し、新しいパスを返す。
    let new_path = path.join("a").join("b");

    // Convert the path into a string slice
    // パスを文字列のスライスに変換する。
    match new_path.to_str() {
        None => panic!("new path is not a valid UTF-8 sequence"),
        Some(s) => println!("new path is {}", s),
    }
}

他のPathメソッド(posix::Pathwindows::Path)をチェックするのを忘れずに!それとMetadata構造体も見ておくことをオススメします。

参照

OsStr and Metadata.

ファイル I/O

File構造体は開かれたファイルを表し(実際にはファイルディスクリプタのラッパーです)、読み込み・書き込み権限のどちらか一方、あるいは両方を提供します。

これはI/Oに関するオペレーションの失敗をより明瞭にします。このおかげでプログラマは直面した失敗を全て見ることができ、より生産的な方法でそれらを扱うことが可能になります。

open

open関数を用いることで読み込み専用モードでファイルを開くことが可能です。

Fileはファイルディスクリプタという資源を保持しており、drop時にはファイルを閉じるところまで面倒を見てくれます。

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    // Create a path to the desired file
    // 目的ファイルに対する`Path`を作成
    let path = Path::new("hello.txt");
    let display = path.display();

    // Open the path in read-only mode, returns `io::Result<File>`
    // pathを読み込み専用モードで開く。これは`io::Result<File>`を返す。
    let mut file = match File::open(&path) {
        // The `description` method of `io::Error` returns a string that
        // describes the error
        // `io::Error`の`description`メソッドはエラーを説明する文字列を返す。
        Err(why) => panic!("couldn't open {}: {}", display, why),
        Ok(file) => file,
    };

    // Read the file contents into a string, returns `io::Result<usize>`
    // ファイルの中身を文字列に読み込む。`io::Result<useize>`を返す。
    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", display, why),
        Ok(_) => print!("{} contains:\n{}", display, s),
    }

    // `file` goes out of scope, and the "hello.txt" file gets closed
    // `file`がスコープから抜け、"hello.txt"が閉じられる。
}

以下が成功時に期待されるアウトプットです。

$ echo "Hello World!" > hello.txt
$ rustc open.rs && ./open
hello.txt contains:
Hello World!

(気が向いたなら、上記の例を様々な形で失敗させてみましょう。例えばhello.txtが存在しないとか、読み込み権限がないとか、そういった状況で実行してみてください。)

create

create関数はファイルを書き込み専用モードで開きます。すでにファイルが存在している場合、破棄して新しい物を作成します。

static LOREM_IPSUM: &str =
    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
";

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    let path = Path::new("lorem_ipsum.txt");
    let display = path.display();

    // Open a file in write-only mode, returns `io::Result<File>`
    // ファイルを書き込み専用モードで開く。返り値は`io::Result<File>`
    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}", display, why),
        Ok(file) => file,
    };

    // Write the `LOREM_IPSUM` string to `file`, returns `io::Result<()>`
    // `LOREM_IPSUM`の文字列を`file`に書き込む。返り値は`io::Result<()>`
    match file.write_all(LOREM_IPSUM.as_bytes()) {
        Err(why) => panic!("couldn't write to {}: {}", display, why),
        Ok(_) => println!("successfully wrote to {}", display),
    }
}

以下は成功時に期待されるアウトプットです。

$ mkdir out
$ rustc create.rs && ./create
successfully wrote to lorem_ipsum.txt
$ cat lorem_ipsum.txt
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

前項の例と同じように、様々な失敗パターンをためしてみることをオススメします。

There is OpenOptions struct that can be used to configure how a file is opened.

read_lines

The method lines() returns an iterator over the lines of a file.

File::open expects a generic, AsRef<Path>. That's what read_lines() expects as input.

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // File hosts must exist in current path before this produces output
    if let Ok(lines) = read_lines("./hosts") {
        // Consumes the iterator, returns an (Optional) String
        for line in lines {
            if let Ok(ip) = line {
                println!("{}", ip);
            }
        }
    }
}

// The output is wrapped in a Result to allow matching on errors
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

Running this program simply prints the lines individually.

$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1

This process is more efficient than creating a String in memory especially working with larger files.

子プロセス

process::Output構造体は終了したプロセスのアウトプットを表し、process::Command構造体はプロセスの作成を行います。

use std::process::Command;

fn main() {
    let output = Command::new("rustc")
        .arg("--version")
        .output().unwrap_or_else(|e| {
            panic!("failed to execute process: {}", e)
    });

    if output.status.success() {
        let s = String::from_utf8_lossy(&output.stdout);

        print!("rustc succeeded and stdout was:\n{}", s);
    } else {
        let s = String::from_utf8_lossy(&output.stderr);

        print!("rustc failed and stderr was:\n{}", s);
    }
}

(余裕があれば、上の例でrustcに不正なフラグを渡し、どうなるか見てみましょう)

パイプ

std::Child構造体は実行中の子プロセスを表します。stdinstdoutstderrを介して表面化のプロセスとのやり取りを仲介します。

use std::error::Error;
use std::io::prelude::*;
use std::process::{Command, Stdio};

static PANGRAM: &'static str =
"the quick brown fox jumped over the lazy dog\n";

fn main() {
    // Spawn the `wc` command
    // `wc`コマンドを起動する。
    let process = match Command::new("wc")
                                .stdin(Stdio::piped())
                                .stdout(Stdio::piped())
                                .spawn() {
        Err(why) => panic!("couldn't spawn wc: {}", why),
        Ok(process) => process,
    };

    // Write a string to the `stdin` of `wc`.
    // `wc`の`stdin`に文字列を書き込む。
    //
    // `stdin` has type `Option<ChildStdin>`, but since we know this instance
    // must have one, we can directly `unwrap` it.
    // `stdin`は`Option<ChildStdin>`型を持つが、今回は値を持っていることが
    // 確かなので、いきなり`unwrap`してしまってよい。
    match process.stdin.unwrap().write_all(PANGRAM.as_bytes()) {
        Err(why) => panic!("couldn't write to wc stdin: {}", why),
        Ok(_) => println!("sent pangram to wc"),
    }

    // Because `stdin` does not live after the above calls, it is `drop`ed,
    // and the pipe is closed.
    //
    // This is very important, otherwise `wc` wouldn't start processing the
    // input we just sent.
    // `stdin`は上のプロセスコールのあとには有効でないので、`drop`され、
    // パイプはcloseされる。
    // (これは非常に重要です。というのもcloseしないと`wc`は
    // 送った値の処理を開始しないからです。)

    // The `stdout` field also has type `Option<ChildStdout>` so must be unwrapped.
    // `stdout`フィールドも`Option<ChildStdout>`型なのでアンラップする必要がある
    let mut s = String::new();
    match process.stdout.unwrap().read_to_string(&mut s) {
        Err(why) => panic!("couldn't read wc stdout: {}", why),
        Ok(_) => print!("wc responded with:\n{}", s),
    }
}

dropの延期

If you'd like to wait for a process::Child to finish, you must call Child::wait, which will return a process::ExitStatus.

use std::process::Command;

fn main() {
    let mut child = Command::new("sleep").arg("5").spawn().unwrap();
    let _result = child.wait().unwrap();

    println!("reached end of main");
}
$ rustc wait.rs && ./wait
# `wait` keeps running for 5 seconds until the `sleep 5` command finishes
reached end of main

ファイルシステムとのやり取り

std::fsモジュールはファイルシステムとやり取りするための関数をいくつか持っています。

use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;

// A simple implementation of `% cat path`
// `% cat path`のシンプルな実装
fn cat(path: &Path) -> io::Result<String> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// A simple implementation of `% echo s > path`
// `% echo s > path`の簡単な実装
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = File::create(path)?;

    f.write_all(s.as_bytes())
}

// A simple implementation of `% touch path` (ignores existing files)
// `% touch path`の簡単な実装(すでにファイルが存在しても無視する。)
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).write(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // Create a directory, returns `io::Result<()>`
    // ディレクトリを作成する。返り値は`io::Result<()>`
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // The previous match can be simplified using the `unwrap_or_else` method
    // 上のmatchは`unwrap_or_else`をメソッドを用いて簡略化できる。
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // Recursively create a directory, returns `io::Result<()>`
    // 再帰的にディレクトリを作成する。返り値は`io::Result<()>`
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s ../b.txt a/c/b.txt`");
    // Create a symbolic link, returns `io::Result<()>`
    // シンボリックリンクを作成、返り値は`io::Result<()>`
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // Read the contents of a directory, returns `io::Result<Vec<Path>>`
    // ディレクトリの内容を読み込む。返り値は`io::Result<Vec<Path>>`
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // Remove a file, returns `io::Result<()>`
    // ファイルを削除。返り値は`io::Result<()>`
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // Remove an empty directory, returns `io::Result<()>`
    // 空のディレクトリを削除。返り値は`io::Result<()>`
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}

以下が成功時に期待されるアウトプットです。

$ rustc fs.rs && ./fs
`mkdir a`
`echo hello > a/b.txt`
`mkdir -p a/c/d`
`touch a/c/e.txt`
`ln -s ../b.txt a/c/b.txt`
`cat a/c/b.txt`
> hello
`ls a`
> "a/b.txt"
> "a/c"
`rm a/c/e.txt`
`rmdir a/c/d`

最終的なaディレクトリの状態は以下です。

$ tree a
a
|-- b.txt
`-- c
    `-- b.txt -> ../b.txt

1 directory, 2 files

An alternative way to define the function cat is with ? notation:

fn cat(path: &Path) -> io::Result<String> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

参照

cfg!

引数処理

Standard Library

コマンドライン引数はstd::env::argsを介して取得できます。これはそれぞれの引数を文字列としてyieldするイテレータを返します。

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    // The first argument is the path that was used to call the program.
    // ひとつ目の引数はプログラムを呼び出す際に使用したパス
    println!("My path is {}.", args[0]);

    // The rest of the arguments are the passed command line parameters.
    // Call the program like this:
    // 残りはプログラムに渡されたコマンドラインパラメータ。
    // プログラムはこんなふうに呼び出す。
    //   $ ./args arg1 arg2
    println!("I got {:?} arguments: {:?}.", args.len() - 1, &args[1..]);
}
$ ./args 1 2 3
My path is ./args.
I got 3 arguments: ["1", "2", "3"].

Crates

Alternatively, there are numerous crates that can provide extra functionality when creating command-line applications. The Rust Cookbook exhibits best practices on how to use one of the more popular command line argument crates, clap.

引数のパース

matchを用いて簡単な引数をパースできます。

use std::env;

fn increase(number: i32) {
    println!("{}", number + 1);
}

fn decrease(number: i32) {
    println!("{}", number - 1);
}

fn help() {
    println!("usage:
match_args <string>
    Check whether given string is the answer.
match_args {{increase|decrease}} <integer>
    Increase or decrease given integer by one.");
}

fn main() {
    let args: Vec<String> = env::args().collect();

    match args.len() {
        // no arguments passed
        // 引数がない場合
        1 => {
            println!("My name is 'match_args'. Try passing some arguments!");
        },
        // one argument passed
        // 引数が1つの場合
        2 => {
            match args[1].parse() {
                Ok(42) => println!("This is the answer!"),
                _ => println!("This is not the answer."),
            }
        },
        // one command and one argument passed
        // コマンドが一つと引数が一つの場合
        3 => {
            let cmd = &args[1];
            let num = &args[2];
            // parse the number
            // 数字をパース
            let number: i32 = match num.parse() {
                Ok(n) => {
                    n
                },
                Err(_) => {
                    eprintln!("error: second argument not an integer");
                    help();
                    return;
                },
            };
            // parse the command
            // コマンドをパース
            match &cmd[..] {
                "increase" => increase(number),
                "decrease" => decrease(number),
                _ => {
                    eprintln!("error: invalid command");
                    help();
                },
            }
        },
        // all the other cases
        // その他の場合
        _ => {
            // show a help message
            // ヘルプメッセージを表示
            help();
        }
    }
}
$ ./match_args Rust
This is not the answer.
$ ./match_args 42
This is the answer!
$ ./match_args do something
error: second argument not an integer
usage:
match_args <string>
    Check whether given string is the answer.
match_args {increase|decrease} <integer>
    Increase or decrease given integer by one.
$ ./match_args do 42
error: invalid command
usage:
match_args <string>
    Check whether given string is the answer.
match_args {increase|decrease} <integer>
    Increase or decrease given integer by one.
$ ./match_args increase 42
43

他言語関数インターフェイス

RustはCのライブラリを呼び出すために他言語関数インターフェイス(Foreign Function Interface, FFI)を持っています。他言語の関数を使用する際には、そのライブラリ名を#[link]アトリビュートに渡し、更にそれでアノテーションされたexternブロック内で宣言する必要があります。

use std::fmt;

// this extern block links to the libm library
// このexternブロックはlibmライブラリをリンクする。
#[link(name = "m")]
extern {
    // this is a foreign function
    // that computes the square root of a single precision complex number
    // 他言語の関数宣言。
    // この関数は単精度浮動小数の複素数型の平方根を計算するためのもの
    fn csqrtf(z: Complex) -> Complex;

    fn ccosf(z: Complex) -> Complex;
}

// Since calling foreign functions is considered unsafe,
// it's common to write safe wrappers around them.
// 型安全ににするためのラッパ
fn cos(z: Complex) -> Complex {
    unsafe { ccosf(z) }
}

fn main() {
    // z = -1 + 0i
    let z = Complex { re: -1., im: 0. };

    // calling a foreign function is an unsafe operation
    let z_sqrt = unsafe { csqrtf(z) };

    println!("the square root of {:?} is {:?}", z, z_sqrt);

    // calling safe API wrapped around unsafe operation
    println!("cos({:?}) = {:?}", z, cos(z));
}

// Minimal implementation of single precision complex numbers
// 単精度浮動小数の複素数型の最小限の実装
#[repr(C)]
#[derive(Clone, Copy)]
struct Complex {
    re: f32,
    im: f32,
}

impl fmt::Debug for Complex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.im < 0. {
            write!(f, "{}-{}i", self.re, -self.im)
        } else {
            write!(f, "{}+{}i", self.re, self.im)
        }
    }
}

テスト

Rustはとても正確性を配慮したプログラミング言語であり、ソフトウェアテストを書くためのサポートを言語自身が含んでいます。

テストには3つの種類があります。

またRustではテストのために追加の依存パッケージを指定することもできます。

参照

Unit testing

Tests are Rust functions that verify that the non-test code is functioning in the expected manner. The bodies of test functions typically perform some setup, run the code we want to test, then assert whether the results are what we expect.

Most unit tests go into a tests mod with the #[cfg(test)] attribute. Test functions are marked with the #[test] attribute.

Tests fail when something in the test function panics. There are some helper macros:

  • assert!(expression) - panics if expression evaluates to false.
  • assert_eq!(left, right) and assert_ne!(left, right) - testing left and right expressions for equality and inequality respectively.
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

// This is a really bad adding function, its purpose is to fail in this
// example.
#[allow(dead_code)]
fn bad_add(a: i32, b: i32) -> i32 {
    a - b
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(1, 2), 3);
    }

    #[test]
    fn test_bad_add() {
        // This assert would fire and test will fail.
        // Please note, that private functions can be tested too!
        assert_eq!(bad_add(1, 2), 3);
    }
}

Tests can be run with cargo test.

$ cargo test

running 2 tests
test tests::test_bad_add ... FAILED
test tests::test_add ... ok

failures:

---- tests::test_bad_add stdout ----
        thread 'tests::test_bad_add' panicked at 'assertion failed: `(left == right)`
  left: `-1`,
 right: `3`', src/lib.rs:21:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    tests::test_bad_add

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

Tests and ?

None of the previous unit test examples had a return type. But in Rust 2018, your unit tests can return Result<()>, which lets you use ? in them! This can make them much more concise.

fn sqrt(number: f64) -> Result<f64, String> {
    if number >= 0.0 {
        Ok(number.powf(0.5))
    } else {
        Err("negative floats don't have square roots".to_owned())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sqrt() -> Result<(), String> {
        let x = 4.0;
        assert_eq!(sqrt(x)?.powf(2.0), x);
        Ok(())
    }
}

See "The Edition Guide" for more details.

Testing panics

To check functions that should panic under certain circumstances, use attribute #[should_panic]. This attribute accepts optional parameter expected = with the text of the panic message. If your function can panic in multiple ways, it helps make sure your test is testing the correct panic.

pub fn divide_non_zero_result(a: u32, b: u32) -> u32 {
    if b == 0 {
        panic!("Divide-by-zero error");
    } else if a < b {
        panic!("Divide result is zero");
    }
    a / b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_divide() {
        assert_eq!(divide_non_zero_result(10, 2), 5);
    }

    #[test]
    #[should_panic]
    fn test_any_panic() {
        divide_non_zero_result(1, 0);
    }

    #[test]
    #[should_panic(expected = "Divide result is zero")]
    fn test_specific_panic() {
        divide_non_zero_result(1, 10);
    }
}

Running these tests gives us:

$ cargo test

running 3 tests
test tests::test_any_panic ... ok
test tests::test_divide ... ok
test tests::test_specific_panic ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests tmp-test-should-panic

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Running specific tests

To run specific tests one may specify the test name to cargo test command.

$ cargo test test_any_panic
running 1 test
test tests::test_any_panic ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out

   Doc-tests tmp-test-should-panic

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

To run multiple tests one may specify part of a test name that matches all the tests that should be run.

$ cargo test panic
running 2 tests
test tests::test_any_panic ... ok
test tests::test_specific_panic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out

   Doc-tests tmp-test-should-panic

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Ignoring tests

Tests can be marked with the #[ignore] attribute to exclude some tests. Or to run them with command cargo test -- --ignored


#![allow(unused)]
fn main() {
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    fn test_add_hundred() {
        assert_eq!(add(100, 2), 102);
        assert_eq!(add(2, 100), 102);
    }

    #[test]
    #[ignore]
    fn ignored_test() {
        assert_eq!(add(0, 0), 0);
    }
}
}
$ cargo test
running 3 tests
test tests::ignored_test ... ignored
test tests::test_add ... ok
test tests::test_add_hundred ... ok

test result: ok. 2 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out

   Doc-tests tmp-ignore

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

$ cargo test -- --ignored
running 1 test
test tests::ignored_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests tmp-ignore

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Documentation testing

The primary way of documenting a Rust project is through annotating the source code. Documentation comments are written in markdown and support code blocks in them. Rust takes care about correctness, so these code blocks are compiled and used as tests.

/// First line is a short summary describing function.
///
/// The next lines present detailed documentation. Code blocks start with
/// triple backquotes and have implicit `fn main()` inside
/// and `extern crate <cratename>`. Assume we're testing `doccomments` crate:
///
/// ```
/// let result = doccomments::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

/// Usually doc comments may include sections "Examples", "Panics" and "Failures".
///
/// The next function divides two numbers.
///
/// # Examples
///
/// ```
/// let result = doccomments::div(10, 2);
/// assert_eq!(result, 5);
/// ```
///
/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust,should_panic
/// // panics on division by zero
/// doccomments::div(10, 0);
/// ```
pub fn div(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Divide-by-zero error");
    }

    a / b
}

Tests can be run with cargo test:

$ cargo test
running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests doccomments

running 3 tests
test src/lib.rs - add (line 7) ... ok
test src/lib.rs - div (line 21) ... ok
test src/lib.rs - div (line 31) ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Motivation behind documentation tests

The main purpose of documentation tests is to serve as examples that exercise the functionality, which is one of the most important guidelines. It allows using examples from docs as complete code snippets. But using ? makes compilation fail since main returns unit. The ability to hide some source lines from documentation comes to the rescue: one may write fn try_main() -> Result<(), ErrorType>, hide it and unwrap it in hidden main. Sounds complicated? Here's an example:

/// Using hidden `try_main` in doc tests.
///
/// ```
/// # // hidden lines start with `#` symbol, but they're still compileable!
/// # fn try_main() -> Result<(), String> { // line that wraps the body shown in doc
/// let res = try::try_div(10, 2)?;
/// # Ok(()) // returning from try_main
/// # }
/// # fn main() { // starting main that'll unwrap()
/// #    try_main().unwrap(); // calling try_main and unwrapping
/// #                         // so that test will panic in case of error
/// # }
/// ```
pub fn try_div(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Divide-by-zero"))
    } else {
        Ok(a / b)
    }
}

See Also

Integration testing

Unit tests are testing one module in isolation at a time: they're small and can test private code. Integration tests are external to your crate and use only its public interface in the same way any other code would. Their purpose is to test that many parts of your library work correctly together.

Cargo looks for integration tests in tests directory next to src.

File src/lib.rs:

// Define this in a crate called `adder`.
// Assume that crate is called adder, will have to extern it in integration test.
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

File with test: tests/integration_test.rs:

// extern crate we're testing, same as any other code would do.
extern crate adder;

#[test]
fn test_add() {
    assert_eq!(adder::add(3, 2), 5);
}

Running tests with cargo test command:

$ cargo test
running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/integration_test-bcd60824f5fbfe19

running 1 test
test test_add ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Each Rust source file in the tests directory is compiled as a separate crate. One way of sharing some code between integration tests is making a module with public functions, importing and using it within tests.

File tests/common.rs:

pub fn setup() {
    // some setup code, like creating required files/directories, starting
    // servers, etc.
}

File with test: tests/integration_test.rs

// extern crate we're testing, same as any other code will do.
extern crate adder;

// importing common module.
mod common;

#[test]
fn test_add() {
    // using common code.
    common::setup();
    assert_eq!(adder::add(3, 2), 5);
}

Modules with common code follow the ordinary modules rules, so it's ok to create common module as tests/common/mod.rs.

Development dependencies

Sometimes there is a need to have dependencies for tests (or examples, or benchmarks) only. Such dependencies are added to Cargo.toml in the [dev-dependencies] section. These dependencies are not propagated to other packages which depend on this package.

One such example is pretty_assertions, which extends standard assert_eq! and assert_ne! macros, to provide colorful diff.
One such example is using a crate that extends standard assert! macros.
File Cargo.toml:

# standard crate data is left out
[dev-dependencies]
pretty_assertions = "1"

File src/lib.rs:

// externing crate for test-only use
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq; // crate for test-only use. Cannot be used in non-test code.

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }
}

See Also

Cargo docs on specifying dependencies.

安全でない操作

この章の内容を見る前に、公式ドキュメントから引用した以下の文章をお読みください。

コードベース中の、アンセーフな操作をするコードの量は、可能な限り小さく無くてはならない。

この戒めを頭に叩き込んだ上で、さあはじめましょう! Rustにおいて、アンセーフなブロックはコンパイラのチェックをスルーするために使われます。具体的には以下の4つの主要なユースケースがあります。

  • 生ポインタのデリファレンス
  • calling functions or methods which are unsafe (including calling a function over FFI, see a previous chapter of the book)
  • accessing or modifying static mutable variables
  • implementing unsafe traits

生ポインタ

生ポインタ*と参照&Tはよく似た機能を持ちますが、後者は必ず有効なデータを指していることが借用チェッカーによって保証されているので、常に安全です。生ポインタのデリファレンスはアンセーフなブロックでしか実行できません。

fn main() {
    let raw_p: *const u32 = &10;

    unsafe {
        assert!(*raw_p == 10);
    }
}

Calling Unsafe Functions

Some functions can be declared as unsafe, meaning it is the programmer's responsibility to ensure correctness instead of the compiler's. One example of this is std::slice::from_raw_parts which will create a slice given a pointer to the first element and a length.

use std::slice;

fn main() {
    let some_vector = vec![1, 2, 3, 4];

    let pointer = some_vector.as_ptr();
    let length = some_vector.len();

    unsafe {
        let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

        assert_eq!(some_vector.as_slice(), my_slice);
    }
}

For slice::from_raw_parts, one of the assumptions which must be upheld is that the pointer passed in points to valid memory and that the memory pointed to is of the correct type. If these invariants aren't upheld then the program's behaviour is undefined and there is no knowing what will happen.

Compatibility

The Rust language is fastly evolving, and because of this certain compatibility issues can arise, despite efforts to ensure forwards-compatibility wherever possible.

Raw identifiers

Rust, like many programming languages, has the concept of "keywords". These identifiers mean something to the language, and so you cannot use them in places like variable names, function names, and other places. Raw identifiers let you use keywords where they would not normally be allowed. This is particularly useful when Rust introduces new keywords, and a library using an older edition of Rust has a variable or function with the same name as a keyword introduced in a newer edition.

For example, consider a crate foo compiled with the 2015 edition of Rust that exports a function named try. This keyword is reserved for a new feature in the 2018 edition, so without raw identifiers, we would have no way to name the function.

extern crate foo;

fn main() {
    foo::try();
}

You'll get this error:

error: expected identifier, found keyword `try`
 --> src/main.rs:4:4
  |
4 | foo::try();
  |      ^^^ expected identifier, found keyword

You can write this with a raw identifier:

extern crate foo;

fn main() {
    foo::r#try();
}

周辺情報

この章では、プログラミングそれ自体に関係はないけれども、色々と人々の役に立つ機能やインフラについて説明していきます。例えば:

  • ドキュメンテーション: Rust付属コマンドrustdocを用いて、ライブラリのドキュメントを生成します。
  • Playpen: Integrate the Rust Playpen(also known as the Rust Playground) in your documentation.

ドキュメンテーション

Use cargo doc to build documentation in target/doc.

Use cargo test to run all tests (including documentation tests), and cargo test --doc to only run documentation tests.

These commands will appropriately invoke rustdoc (and rustc) as required.

Doc comments

ドキュメンテーションコメントとはrustdocを使用した際にドキュメントにコンパイルされるコメントのことです。///によって普通のコメントと区別され、ここではMarkdownを使用することができます。ドキュメンテーションコメントは大規模なプロジェクトの際に非常に有用です。

#![crate_name = "doc"]

/// A human being is represented here
/// あらゆる人物はここに代表されます。
pub struct Person {
    /// A person must have a name, no matter how much Juliet may hate it
    /// ジュリエットがどんなに名前というものを嫌っていようと、
    /// 人物には名前が必要です。
    name: String,
}

impl Person {
    /// Returns a person with the name given them
    /// 与えられた名前を持つpersonをを返します。
    ///
    /// # Arguments
    ///
    /// * `name` - A string slice that holds the name of the person
    /// * `name` - `person`の名前を表す文字列のスライス
    ///
    /// # Examples
    ///
    /// ```
    /// // You can have rust code between fences inside the comments
    /// // If you pass --test to `rustdoc`, it will even test it for you!
    /// // バッククォートによってRustのコードをコメント中に挟むこと
    /// // もできます。`rustdoc`に--testを渡せば、テストも行えます!
    /// // (訳注: pythonのdoctestと同じです。)
    /// use doc::Person;
    /// let person = Person::new("name");
    /// ```
    pub fn new(name: &str) -> Person {
        Person {
            name: name.to_string(),
        }
    }

    /// Gives a friendly hello!
    /// フレンドリーに挨拶しましょう!
    ///
    /// Says "Hello, [name]" to the `Person` it is called on.
    /// このメソッドを呼び出した`Person`に対して"Hello, [name]"
    /// と話しかけます。
    pub fn hello(& self) {
        println!("Hello, {}!", self.name);
    }
}

fn main() {
    let john = Person::new("John");

    john.hello();
}

To run the tests, first build the code as a library, then tell rustdoc where to find the library so it can link it into each doctest program:

$ rustc doc.rs --crate-type lib
$ rustdoc --test --extern doc="libdoc.rlib" doc.rs

Doc attributes

Below are a few examples of the most common #[doc] attributes used with rustdoc.

inline

Used to inline docs, instead of linking out to separate page.

#[doc(inline)]
pub use bar::Bar;

/// bar docs
mod bar {
    /// the docs for Bar
    pub struct Bar;
}

no_inline

Used to prevent linking out to separate page or anywhere.

// Example from libcore/prelude
#[doc(no_inline)]
pub use crate::mem::drop;

hidden

Using this tells rustdoc not to include this in documentation:

// Example from the futures-rs library
#[doc(hidden)]
pub use self::async_await::*;

For documentation, rustdoc is widely used by the community. It's what is used to generate the std library docs.

参照

Playpen

The Rust Playpen is a way to experiment with Rust code through a web interface. This project is now commonly referred to as Rust Playground.

Using it with mdbook

In mdbook, you can make code examples playable and editable.

fn main() {
    println!("Hello World!");
}

This allows the reader to both run your code sample, but also modify and tweak it. The key here is the adding the word editable to your codefence block separated by a comma.

```rust,editable
//...place your code here
```

Additionally, you can add ignore if you want mdbook to skip your code when it builds and tests.

```rust,editable,ignore
//...place your code here
```

Using it with docs

You may have noticed in some of the official Rust docs a button that says "Run", which opens the code sample up in a new tab in Rust Playground. This feature is enabled if you use the #[doc] attribute called html_playground_url.

See also:

関連キーワード:  println, let, function, self, type, 関数, use, 参照, struct, Result