Rust by Example
Rust は安全性、速度、並列性にフォーカスした現代的なシステムプログラミング用のプログラミング言語です。ガベージコレクション無しでメモリ安全であることが、これを可能にしています。
Rust by Example(RBE)はRustの実行可能なサンプルスクリプト集で、ここではRustの様々なコンセプトと標準ライブラリを紹介していきます。この例をより活用するためにはRustをローカルにインストールし、公式ドキュメントをチェックすることをおすすめします。興味がある方はこのサイト自体のソースのチェックもどうぞ。
それでははじめましょう!
-
Hello World - お決まりのHello Worldプログラムから始めましょう。
-
基本データ型 - 符号付き整数や符号無し整数、その他の基本データ型について学びましょう。
-
カスタム型 -
structとenumについて。 -
変数の束縛 - ミュータブルな束縛、スコープ、シャドーイングについて。
-
型 - 型を変更したり定義したりすることを学びましょう。
-
型変換 - 文字列や整数、浮動小数点数など様々な型から型への変換について。
-
式 - 式とその使い方について学びましょう。
-
制御フロー -
ifやelse、forなど。 -
関数 - メソッド、クロージャ、高階関数について。
-
モジュール - プログラムをモジュールを使って整理しましょう。
-
クレート - クレートは、Rustにおいてコンパイルされる単位です。ライブラリの作り方について学びます。
-
Cargo - Rustの公式パッケージマネージャの基本的な機能を学びます。
-
アトリビュート - アトリビュートは、モジュールやクレート、要素に適用されるメタデータです。
-
ジェネリクス - 様々な型の引数を取れる関数やデータ型を書く方法を学びましょう。
-
スコープの規則 - スコープは所有権、借用、ライフタイムにおいて重要な役割を果たします。
-
トレイト - トレイトとは、未知の型
Selfに対して定義された一連のメソッドです。 -
マクロ - マクロはコードを書くためのコードです。メタプログラミングとしても知られています。
-
エラーハンドリング - 失敗に対処するRust流のやり方を学びましょう。
-
標準ライブラリの型 -
stdライブラリによって提供されるいくつかのカスタム型について学びます。 -
標準ライブラリのその他 - ファイルハンドリングとスレッドのためのカスタム型について。
-
テスト - Rustにおけるテストのすべて。
-
安全でない操作 - 安全でない操作について学びましょう。
-
互換性 - Rustの進化と互換性について。
-
周辺情報 - ドキュメント、ベンチマークの方法。
Hello World
ここでは伝統的な “Hello World!” プログラムのソースを紹介します。
// これはコメントです。コンパイラによって無視されます。
// 右にある「Run」ボタンからこのコードをテストできます。
// キーボードを使いたければ「Ctrl + Enter」もOKです。
// このコードは編集可能です。ぜひハックしてみましょう!
// 「Reset」ボタンでいつでも元のコードに戻すことができます ->
// main関数です。
fn main() {
// コンパイルされたバイナリが実行されるとこの関数が呼び出されます。
// コンソールに文字列を出力します。
println!("Hello World!");
}
println!は文字列をコンソールに出力するための マクロ です。
バイナリファイルはrustcと呼ばれるRustコンパイラを用いて生成することができます。
$ rustc hello.rs
するとhelloという名前の実行可能なバイナリファイルができます。
$ ./hello
Hello World!
演習
上に書いている ‘Run’ をクリックしてアウトプットを見てみましょう。次に、println!マクロをもう一行追加してアウトプットがどうなるか見てみましょう。
Hello World!
I'm a Rustacean!
コメント
あらゆるプログラムにはコメントが必要です。Rustには何種類かのコメントがあります
Regular Comments
These are ignored by the compiler:
- Line comments: Start with
//and continue to the end of the line - Block comments: Enclosed in
/* ... */and can span multiple lines
Documentation Comments (Doc Comments) which are parsed into HTML library documentation:
///- Generates docs for the item that follows it//!- Generates docs for the enclosing item (typically used at the top of a file or module)
fn main() {
// Line comments start with two slashes.
// Everything after the slashes is ignored by the compiler.
// Example: This line won't execute
// println!("Hello, world!");
// Try removing the slashes above and running the code again.
/*
* Block comments are useful for temporarily disabling code.
* They can also be nested: /* like this */ which makes it easy
* to comment out large sections quickly.
*/
/*
Note: The asterisk column on the left is just for style -
it's not required by the language.
*/
// Block comments make it easy to toggle code on/off by adding
// or removing just one slash:
/* <- Add a '/' here to uncomment the entire block below
println!("Now");
println!("everything");
println!("executes!");
// Line comments inside remain unaffected
// */
// Block comments can also be used within expressions:
let x = 5 + /* 90 + */ 5;
println!("Is `x` 10 or 100? x = {}", x);
}
参照
フォーマットして出力
Printing is handled by a series of macros defined in std::fmt some of which are:
format!:フォーマットされたテキストをStringに書き込みます。print!:format!と同様ですが、コンソール (io::stdout) にそのテキストを出力します。println!:print!と同じですが改行が付け加えられます。eprint!:format!と同様ですが、標準エラー出力 (io::stderr) にそのテキストを出力します。eprintln!:eprint!と同じですが改行が付け加えられます。
すべて同じやり方でテキストをパースし、正しくフォーマットできるかコンパイル時にチェックします。
fn main() {
// 一般的に`{}`はどんな引数であろうと自動的に置き換えられます。
// 例えば以下は文字列に変換されます。
println!("{} days", 31);
// 位置引数を利用できます。
// `{}`の内側に整数を指定することで、どの引数で置換されるかが決まります。
// 引数は0から始まります。
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
// 名前での指定も可能です。
println!("{subject} {verb} {object}",
object="the lazy dog",
subject="the quick brown fox",
verb="jumps over");
// `:`のあとにフォーマット文字を指定して
// 異なるフォーマットにすることも可能です。
println!("Base 10: {}", 69420); // 69420
println!("Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("Base 8 (octal): {:o}", 69420); // 207454
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c
// 特定の幅に右詰めすることもできます。この出力は " 1" になります。
// (4つの空白と"1"で合計幅は5です)
println!("{number:>5}", number=1);
// 数字を0埋めすることもできます。
println!("{number:0>5}", number=1); // 00001
// 記号を反対にすると左寄せになり、"10000"が出力されます。
println!("{number:0<5}", number=1); // 10000
// フォーマット指定子の中に`$`をつけることで名前付き引数を利用できます。
println!("{number:0>width$}", number=1, width=5);
// 引数の数が正しいかのチェックも行ってくれます。
println!("My name is {0}, {1} {0}", "Bond");
// FIXME ^ 不足している引数 "James" を追加しましょう。
// `{}`でフォーマットできるのは、fmt::Displayを実装している型のみです。
// ユーザーが定義した型はデフォルトではfmt::Displayを実装していません。
#[allow(dead_code)] // 未使用モジュールを警告する`dead_code`を無効化。
struct Structure(i32);
// `Structure`はfmt::Displayを実装していないので、
// 以下はコンパイルできません。
// println!("This struct `{}` won't print...", Structure(3));
// TODO ^ この行をアンコメントしてみましょう。
// Rust 1.58以上では、周囲の変数から直接引数に取ることができます。
// 上で見たように、以下のコードは4つのスペースと1を、" 1" と出力します。
let number: f64 = 1.0;
let width: usize = 5;
println!("{number:>width$}");
}
std::fmtはいくつものトレイトを持ち、それによってどのようにディスプレイに表示されるかが決まります。特に大事な形式は以下の2つです。
fmt::Debug:{:?}というマーカーを使用し、デバッグ目的に使われます。fmt::Display:{}というマーカーを使用し、より美しく、ユーザフレンドリーに表示します。
この例で用いられている型は、標準ライブラリに含まれているため、ここではfmt::Displayを使用しています。カスタム型をテキストとして表示する場合は、さらに手順が必要です。
fmt::Displayトレイトを実装すると、自動的にToStringトレイトが実装されます。これによりString型への型変換ができるようになります。
In line 43, #[allow(dead_code)] is an attribute which only applies to the item after it.
演習
- 上の例(FIXME を参照)を実行した際に生じるエラーを修復しましょう。
Structure構造体をフォーマットする行をアンコメントしてみましょう。(TODO を参照)println!マクロを追加し、表示される小数部の桁数を調整してPi is roughly 3.142という文字列を出力しましょう。ただし、円周率の値はlet pi = 3.141592を使ってください。(ヒント:小数部の桁数を調整する方法については、std::fmtをチェックする必要があるかもしれません。)
参照
std::fmt, マクロ, 構造体, トレイト, dead_code
Debug
std::fmtのフォーマット用トレイトを使用したい型は、出力できるように実装されている必要があります。stdライブラリの型のように自動で出力可能なものもありますが、他はすべて 手動で実装する必要があります。
fmt::Debugというトレイトはこれを簡略化します。 すべての 型はfmt::Debugの実装を導出(derive)、(すなわち自動で作成)することができるためです。fmt::Displayの場合はやはり手動で実装しなくてはなりません。
#![allow(unused)]
fn main() {
// この構造体は`fmt::Display`、`fmt::Debug`のいずれによっても
// 出力することができません。
struct UnPrintable(i32);
// `derive`アトリビュートは、
// この構造体を`fmt::Debug`で出力するための実装を自動で提供します。
#[derive(Debug)]
struct DebugPrintable(i32);
}
stdライブラリの型の場合は、自動的に{:?}により出力可能になっています。
// `Structure`という構造体のための`fmt::Debug`を導出しています。
// `Structure`は単一の`i32`をメンバに持っています。
#[derive(Debug)]
struct Structure(i32);
// `Deep`という構造体の中に`Structure`を入れ、これを出力可能にしています。
#[derive(Debug)]
struct Deep(Structure);
fn main() {
// `{:?}`による出力は`{}`に似ています。
println!("{:?} months in a year.", 12);
println!("{1:?} {0:?} is the {actor:?} name.",
"Slater",
"Christian",
actor="actor's");
// `Structure`は出力可能です!
println!("Now {:?} will print!", Structure(3));
// `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 };
// 見栄えのよい出力
println!("{:#?}", peter);
}
手動でfmt::Displayを実装することで出力結果を思い通りにできます。
参照
アトリビュート, derive, std::fmt, 構造体
Display
fmt::Debugはコンパクトでクリーンであるようには見えませんね。大抵の場合は、アウトプットの見た目をカスタマイズしたほうが好ましいでしょう。これは{}を使用するfmt::Displayを手動で実装することで可能です。実装はこのようになります。
#![allow(unused)]
fn main() {
// (`use`を使用し、)`fmt`モジュールをインポートします。
use std::fmt;
// `fmt::Display`を実装するための構造体を定義します。
// これは`Structure`と名付けられた、`i32`を含むタプル構造体です。
struct Structure(i32);
// `{}` というマーカーを使用するためには、
// この型専用の`fmt::Display`というトレイトが実装されていなくてはなりません。
impl fmt::Display for Structure {
// このトレイトは`fmt`が正確にこの通りのシグネチャであることを要求します。
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 最初の要素だけを、与えられた出力ストリーム`f`に書き込みます。
// 操作が成功したかどうかを表す`fmt::Result`を返します。
// `write!`は`println!`に非常によく似た文法を使用していることに注目。
write!(f, "{}", self.0)
}
}
}
fmt::Displayはfmt::Debugより綺麗かもしれませんが、stdライブラリの場合は問題が生じます。曖昧な型はどのように表示すれば良いでしょう?例えば、stdライブラリがあらゆるVec<T>に対して単一のスタイルを提供していた場合、どのようなスタイルに整形すればよいでしょう?以下の2つのどちらかを選ぶべきでしょうか?
Vec<path>:/:/etc:/home/username:/bin(:で分割)Vec<number>:1,2,3(,で分割)
答えはNOです。あらゆる型に対して理想的なスタイルなどというものはありませんし、stdライブラリによってそれが提供されているわけでもありません。fmt::DisplayはVec<T>のようなジェネリックなコンテナ用に定義されているわけではありませんので、このような場合はfmt::Debugを使用するべきです。
ジェネリック でない コンテナ型の場合は、このような問題は生じませんので問題なくfmt::Displayを実装することができます。
use std::fmt; // `fmt`のインポート
// 2つの数字を扱うための構造体です。出力を`Display`と比較するため`Debug`
// を導出しています。
#[derive(Debug)]
struct MinMax(i64, i64);
// `MinMax`用の`Display`を実装しています。
impl fmt::Display for MinMax {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// `self.number`でそれぞれのデータポイントを参照できます。
write!(f, "({}, {})", self.0, self.1)
}
}
// 比較のため、フィールドに名前をつけられる様な構造体を定義しましょう。
#[derive(Debug)]
struct Point2D {
x: f64,
y: f64,
}
// 先程と同様にして、`Point2D`用の`Display`を実装しています。
impl fmt::Display for Point2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// `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);
// The following line would not compile: both `Debug` and `Display`
// were implemented, but `{:b}` requires `fmt::Binary` to be
// implemented, which it hasn't been for `Point2D`.
// 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 }
Display: 4.7 -2.3i
Debug: Complex { real: 4.7, imag: -2.3 }
Bonus: Add a space after the +/- signs.
Hints in case you get stuck:
- Check the documentation for
Sign/#/0instd::fmt. - Bonus: Check
if-elsebranching and theabsfunction.
参照
導出, std::fmt, マクロ, 構造体, トレイト, use
テストケース:リスト
構造体のそれぞれの要素を別々に扱うfmt::Displayを実装するのはトリッキーです。というのも、それぞれのwrite!が別々のfmt::Resultを生成するためです。適切に処理するためには すべての 結果に対して処理を書かなくてはなりません。このような場合は?演算子が使えます。
以下のように?をwrite!に対して使用します。
// `write!`を実行し、エラーが生じた場合はエラーを返します。そうでなければ実行を継続します。
write!(f, "{}", value)?;
?を使用すれば、Vec用のfmt::Displayはより簡単に実装できます。
use std::fmt; // `fmt`モジュールのインポート
// `Vec`を含む`List`という名の構造体を定義。
struct List(Vec<i32>);
impl fmt::Display for List {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Create a reference to the Vec<i32> stored in the List struct.
let vec = &self.0;
write!(f, "[")?;
// Iterate over `v` in `vec` while enumerating the iteration
// index in `index`.
for (index, v) in vec.iter().enumerate() {
// 先頭以外の全要素にカンマを付けます。
// ?演算子を使ってエラーを返します。
if index != 0 { write!(f, ", ")?; }
write!(f, "{}", v)?;
}
// 開きっぱなしのブラケットを閉じて、`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)という単一の変数がX、o、 指定なし 、という様々な 引数タイプ に応じてフォーマットされています。
フォーマットの機能はそれぞれの引数タイプごとに個別のトレイトを用いて実装されています。最も一般的なトレイトはDisplayで、これは引数タイプが未指定(たとえば{})の時に呼び出されます。
use std::fmt::{self, Formatter, Display};
struct City {
name: &'static str,
// 緯度
lat: f32,
// 経度
lon: f32,
}
impl Display for City {
// `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!`は`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 },
] {
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 },
] {
// fmt::Displayに実装を追加したら、 {} を使用するように変更してください。
println!("{:?}", color);
}
}
フォーマット用トレイトの全リストはこちらから、引数タイプについてはstd::fmtのドキュメンテーションから参照できます。
演習
上にあるソースコード中のColorという構造体のためのfmt::Displayトレイトの実装を追加しましょう。出力は以下のようになるはずです。
RGB (128, 255, 90) 0x80FF5A
RGB (0, 3, 254) 0x0003FE
RGB (0, 0, 0) 0x000000
Two hints if you get stuck:
- それぞれの色を2回以上記述する必要があるかもしれません。
- You can pad with zeros to a width of 2 with
:0>2. For hexadecimals, you can use:02X.
Bonus:
- If you would like to experiment with type casting in advance, the formula for calculating a color in the RGB color space is
RGB = (R * 65_536) + (G * 256) + B, whereR is RED, G is GREEN, and B is BLUE. An unsigned 8-bit integer (u8) can only hold numbers up to 255. To castu8tou32, you can writevariable_name as u32.
参照
基本データ型
Rustは様々な基本データ型の使用をサポートしています。以下がその例です。
スカラー型
- 符号付き整数:
i8,i16,i32,i64,i128,isize(ポインタのサイズ) - 符号無し整数:
u8,u16,u32,u64,u128,usize(ポインタのサイズ) - 浮動小数点数:
f32,f64 char:'a','α','∞'などのUnicodeのスカラー値(それぞれ4バイト)bool:trueまたはfalse- ユニット型
():唯一の値として空のタプル()を持つ
ユニット型はその値がタプルですが、複合型とはみなされません。内部に複数の値を含んでいるわけではないからです。
複合型
- 配列: 例えば
[1, 2, 3] - タプル:例えば
(1, true)
変数は常に 型指定 できます。数値型の場合はさらにサフィックスでの指定も可能です。指定しない場合デフォルトになります。整数はi32が、浮動小数点はf64がデフォルトです。また、Rustは文脈から型を推論することもできます。
fn main() {
// 変数に型を指定。
let logical: bool = true;
let a_float: f64 = 1.0; // 通常の型指定
let an_integer = 5i32; // サフィックスによる型指定
// サフィックスを指定しない場合、デフォルトを選択。
let default_float = 3.0; // `f64`
let default_integer = 7; // `i32`
// 型を文脈から推論することも可能。
let mut inferred_type = 12; // 型 i64 は次行の内容に基づいて推論。
inferred_type = 4294967296i64;
// ミュータブルな変数は値を変更できます。
let mut mutable = 12; // ミュータブルな`i32`
mutable = 21;
// エラー!ミュータブルな変数でも型は不変。
mutable = true;
// 変数はシャドーイングによって上書きできます。
let mutable = true;
/* Compound types - Array and Tuple */
// Array signature consists of Type T and length as [T; length].
let my_array: [i32; 5] = [1, 2, 3, 4, 5];
// Tuple is a collection of values of different types
// and is constructed using parentheses ().
let my_tuple = (5u32, 1u8, true, -5.04f32);
}
参照
リテラルと演算子
整数1、浮動小数点数1.2、文字'a'、文字列"abc"、ブーリアンtrue、ユニット()は、リテラルを使って表すことが可能です。
また整数型の場合、プレフィックスに0x、0o、0bを指定することでそれぞれ16進数、8進数、2進数を使って表すことができます。
可読性のため、_(アンダースコア)を数値リテラルの間に挿入することができます。例えば1_000は1000と、0.000_001は0.000001とそれぞれ同一です。
また、Rustは1e6や7.6e-4などの科学的なE表記をサポートしています。この表記はf64になります。
コンパイラに、リテラルの型を伝えたい場合があります。現在の仕様では、リテラルが32ビット符号無し整数であることを伝える場合、u32サフィックスを、符号付き32ビット整数であればi32サフィックスを使用します。
Rustで使用可能な演算子とその優先順位は、Cなどの言語のものとほぼ同じです。
fn main() {
// 整数の足し算
println!("1 + 2 = {}", 1u32 + 2);
// 整数の引き算
println!("1 - 2 = {}", 1i32 - 2);
// TODO ^ 型が重要であることを実感するため`1i32`を`1u32`に変更してみましょう。
// 科学的表記
println!("1e4 is {}, -2.5e-3 is {}", 1e4, -2.5e-3);
// 短絡評価できる論理演算子
println!("true AND false is {}", true && false);
println!("true OR false is {}", true || false);
println!("NOT true is {}", !true);
// ビットワイズ演算
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);
// 可読性のための`_`(アンダースコア)の使用
println!("One million is written as {}", 1_000_000u32);
}
タプル
タプルは異なる型の値の集合です。括弧()を用いて生成します。タプル自体がそのメンバに対する型シグネチャを保持していますので、明示すると(T1, T2, ...)のようになります。タプルは大きさに制限がありませんので、関数が複数の値を返したい時に使われます。
// タプルを関数の引数と返り値として使用しています。
fn reverse(pair: (i32, bool)) -> (bool, i32) {
// `let`でタプルの中の値を別の変数に束縛することができます。
let (int_param, bool_param) = pair;
(bool_param, int_param)
}
// 以下の構造体は後ほど「演習」で用います。
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);
fn main() {
// 様々な型を値に持つタプル
let long_tuple = (1u8, 2u16, 3u32, 4u64,
-1i8, -2i16, -3i32, -4i64,
0.1f32, 0.2f64,
'a', true);
// インデックスを用いて、タプル内の要素を参照できます。
println!("Long tuple first value: {}", long_tuple.0);
println!("Long tuple second value: {}", long_tuple.1);
// タプルはタプルのメンバになれます。
let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
// タプルは出力できます。
println!("tuple of tuples: {:?}", tuple_of_tuples);
// しかし長すぎるタプル(12要素より多いもの)は出力できません。
//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 ^ 上記2行のコメントを外して、コンパイルエラーを確認しましょう。
let pair = (1, true);
println!("Pair is {:?}", pair);
println!("The reversed pair is {:?}", reverse(pair));
// 要素を1つしか持たないタプルを作成する場合、括弧で囲まれたただのリテラル
// と区別するため、カンマが必要になります。
println!("One element tuple: {:?}", (5u32,));
println!("Just an integer: {:?}", (5u32));
// タプルを分解して別の変数にそれぞれの値を代入。
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);
}
演習
-
復習 :上にある
Matrixという構造体に、fmt::Displayトレイトを追加しましょう。デバッグフォーマット{:?}ではなくディスプレイフォーマット{}で出力すれば次のようになるはずです。( 1.1 1.2 ) ( 2.1 2.2 )必要に応じてディスプレイのページに戻りましょう。
-
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;
// この関数はスライスを借用します。
fn analyze_slice(slice: &[i32]) {
println!("First element of the slice: {}", slice[0]);
println!("The slice has {} elements", slice.len());
}
fn main() {
// 固定長の配列(型シグネチャは冗長なので、なくても可)
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// すべての要素を同じ値で初期化する場合
let ys: [i32; 500] = [0; 500];
// インデックスは0から。
println!("First element of the array: {}", xs[0]);
println!("Second element of the array: {}", xs[1]);
// `len`は配列の要素数を返します。
println!("Number of elements in array: {}", xs.len());
// 配列はスタック上に置かれます。
println!("Array occupies {} bytes", mem::size_of_val(&xs));
// 配列は自動的にスライスとして借用されます。
println!("Borrow the whole array as a slice.");
analyze_slice(&xs);
// スライスは配列の一部を指すことができます。
// [starting_index..ending_index] の形をとり、
// `starting_index`はスライスの先頭の位置を表し、
// `ending_index`はスライスの末尾の1つ先の位置を表します。
println!("Borrow a section of the array as a slice.");
analyze_slice(&ys[1 .. 4]);
// 空のスライス`&[]`の例:
let empty_array: [u32; 0] = [];
assert_eq!(&empty_array, &[]);
assert_eq!(&empty_array, &[][..]); // 同じ意味だがより冗長な書き方
// 配列は、`Option`を返す`.get`で安全にアクセスできます。
// `Option`は以下のようにマッチさせることもできるし、
// 運よく処理を続ける代わりに、`.expect()`で素敵なメッセージとともに
// プログラムを終了することもできます。
for i in 0..xs.len() + 1 { // おっと、1要素余分!
match xs.get(i) {
Some(xval) => println!("{}: {}", i, xval),
None => println!("Slow down! {} is too far!", i),
}
}
// Out of bound indexing on array with constant value causes compile time error.
//println!("{}", xs[5]);
// Out of bound indexing on slice causes runtime error.
//println!("{}", xs[..][5]);
}
カスタム型
Rustでのカスタムデータ型の作成は主に以下の2つのキーワードを介して行われます。
struct:構造体を定義するenum:列挙型を定義する
const、あるいはstaticというキーワードによって定数を定義することもできます。
構造体
structというキーワードを用いて作成できる構造体には3種類あります。
- タプル構造体。(すなわちタプルに名前が付いたようなもの)
- クラシックなC言語スタイルの構造体。
- ユニット構造体。これはフィールドを持たず、ジェネリック型を扱う際に有効です。
// 使用されていないコードよる警告を隠すアトリビュート
#![allow(dead_code)]
#[derive(Debug)]
struct Person {
name: String,
age: u8,
}
// ユニット構造体
struct Unit;
// タプル構造体
struct Pair(i32, f32);
// 2つのフィールドを持つ(クラシックな)構造体
struct Point {
x: f32,
y: f32,
}
// 構造体は他の構造体のフィールドになることができます。
struct Rectangle {
// 長方形は座標空間上における左上隅と右下隅の位置によって指定できます。
top_left: Point,
bottom_right: Point,
}
fn main() {
// 構造体をフィールド初期化の簡略記法で生成。
let name = String::from("Peter");
let age = 27;
let peter = Person { name, age };
// 構造体のデバッグ表示
println!("{:?}", peter);
// `Point`のインスタンス化
let point: Point = Point { x: 5.2, y: 0.4 };
let another_point: Point = Point { x: 10.3, y: 0.2 };
// pointのフィールドにアクセスします。
println!("point coordinates: ({}, {})", point.x, point.y);
// 構造体の更新記法を用いて、別の構造体のフィールドの値を基に
// 新たなpointを生成。
let bottom_right = Point { x: 10.3, ..another_point };
// `bottom_right.y`の値は`another_point.y`と同一ですが、
// これは`another_point`のフィールドの値を用いて生成したためです。
println!("second point: ({}, {})", bottom_right.x, bottom_right.y);
// `let`を使用してpointをデストラクトします。
let Point { x: left_edge, y: top_edge } = point;
let _rectangle = Rectangle {
// 構造体の定義とインスタンスの作成を同時に行います。
top_left: Point { x: left_edge, y: top_edge },
bottom_right: bottom_right,
};
// ユニット構造体のインスタンス化
let _unit = Unit;
// タプル構造体のインスタンス化
let pair = Pair(1, 0.1);
// タプル構造体のフィールドにアクセス
println!("pair contains {:?} and {:?}", pair.0, pair.1);
// タプル構造体のデストラクト
let Pair(integer, decimal) = pair;
println!("pair contains {:?} and {:?}", integer, decimal);
}
演習
Rectangleの面積を計算するrect_area関数を追加してください。ネストしたデストラクトを使ってみましょう。Pointとf32を引数とし、Rectangleを返すsquare関数を追加してください。Rectangleの左上の点がPointになり、f32がRectangleの幅と高さになります。
参照
列挙型
列挙型(enum)はいくつかの異なる要素型の中から1つを選ぶような場合に使用します。構造体(struct)の定義を満たすものならば何でもenum の要素型として使用できます。
// `enum`を作成してwebイベントを分類します。
// 名前と型情報を併せたものが要素型になっていることに注意。
// `PageLoad != PageUnload`、
// `KeyPress(char) != Paste(String)`です。
// 要素型は互いに異なり、互いに非依存です。
enum WebEvent {
// `enum`要素型はユニット風でも構いません。
PageLoad,
PageUnload,
// タプル構造体風
KeyPress(char),
Paste(String),
// C言語スタイルの構造体風
Click { x: i64, y: i64 },
}
// 引数として`WebEvent`列挙型をとり、何も返さない関数
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unloaded"),
// `enum`の要素型から`c`をデストラクトします。
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
// `Click`を`x`と`y`にデストラクトします。
WebEvent::Click { x, y } => {
println!("clicked at x={}, y={}.", x, y);
},
}
}
fn main() {
let pressed = WebEvent::KeyPress('x');
// `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,
}
// 型エイリアスを作成します。
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;
fn main() {
// 長くて不便な列挙型の名前ではなく、別名を使って要素型を参照できます。
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, 関数, 文字列, “Type alias enum variants” RFC
use
The use declaration can be used to avoid typing the full module path to access a name:
// 使用されていないコードよる警告を隠すアトリビュート
#![allow(dead_code)]
enum Stage {
Beginner,
Advanced,
}
enum Role {
Student,
Teacher,
}
fn main() {
// `use`することで絶対名でなくとも使用可能になります。
use Stage::{Beginner, Advanced};
// `Role`の中の名前をすべて`use`します。
use Role::*;
// `use`しているため、`Stage::Beginner`と同じです。
let stage = Beginner;
// `Role::Student`と同じ
let role = Student;
match stage {
// `use`しているのでスコープを明示していません。
Beginner => println!("Beginners are starting their learning journey!"),
Advanced => println!("Advanced learners are mastering their subjects..."),
}
match role {
// こちらも同じ
Student => println!("Students are acquiring knowledge!"),
Teacher => println!("Teachers are spreading knowledge!"),
}
}
参照
C言語ライクな列挙型
列挙型はC言語の列挙型のような使い方をする事もできます。
// 使用されていないコードよる警告を隠すアトリビュート
#![allow(dead_code)]
// 値を明示しない場合、0から整数が順に入ります。
enum Number {
Zero,
One,
Two,
}
// 値を明示する場合
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
fn main() {
// 列挙型の中身は整数としてキャストできます。
println!("zero is {}", Number::Zero as i32);
println!("one is {}", Number::One as i32);
println!("roses are #{:06x}", Color::Red as u32);
println!("violets are #{:06x}", Color::Blue as u32);
}
参照
テストケース:連結リスト
enumの使用が適切なパターンのひとつに、連結リストを作成する場合があります。
use crate::List::*;
enum List {
// Cons:要素をラップし、次の要素へのポインタを保持するタプル構造体
Cons(u32, Box<List>),
// Nil:連結リストの終端であることを示すノード
Nil,
}
// 列挙型にはメソッドを付与することができます。
impl List {
// 空リストの作成
fn new() -> List {
// `Nil`は`List`型を持ちます。
Nil
}
// リストを受け取り、その始端に新しい要素を付加したものを返す関数
fn prepend(self, elem: u32) -> List {
// この`Cons`自体も、その第2要素もどちらもList型です。
Cons(elem, Box::new(self))
}
// リストの長さを返すメソッド
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
match *self {
// Can't take ownership of the tail, because `self` is borrowed;
// instead take a reference to the tail
// And it's a non-tail recursive call which may cause stack overflow for long lists.
Cons(_, ref tail) => 1 + tail.len(),
// 空リストならば長さは0
Nil => 0
}
}
// リストをヒープ上の文字列として表したものを返すメソッド
fn stringify(&self) -> String {
match *self {
Cons(head, ref tail) => {
// `format!`は`print!`に似ていますが、コンソール上に出力
// する代わりに、ヒープ上の文字列を返します。
format!("{}, {}", head, tail.stringify())
},
Nil => {
format!("Nil")
},
}
}
}
fn main() {
// 空の連結リストを作成。
let mut list = List::new();
// 要素を追加。
list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);
// 追加後の状態を表示。
println!("linked list has length: {}", list.len());
println!("{}", list.stringify());
}
参照
定数
Rustには2種類の定数があり、いずれもグローバルスコープを含む任意のスコープで宣言することができます。また、いずれも型を明示しなくてはなりません。
const:不変の値(通常はこちらを使用します)static:'staticライフタイムを持つ変更可能な値。スタティックライフタイムは推論され、明示する必要はありません。可変なスタティック値へのアクセスや変更は安全ではありません。
// グローバル変数はあらゆるスコープの外で宣言します。
static LANGUAGE: &str = "Rust";
const THRESHOLD: i32 = 10;
fn is_big(n: i32) -> bool {
// 関数内から定数を参照。
n > THRESHOLD
}
fn main() {
let n = 16;
// main 関数の中から定数を参照。
println!("This is {}", LANGUAGE);
println!("The threshold is {}", THRESHOLD);
println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
// エラー!const`は変更できません。
THRESHOLD = 5;
// FIXME ^ この行をコメントアウトしましょう
}
参照
const/static のRFC, 'static ライフタイム
変数束縛
Rustは静的な型付けによる型安全性を提供します。変数束縛は宣言時に型を指定できます。とはいえたいていの場合は、コンパイラは変数の型をコンテキストから推測することができますので、型指定の負担を大幅に軽減できます。
値(リテラルなど)はletを用いて変数に束縛することができます。
fn main() {
let an_integer = 1u32;
let a_boolean = true;
let unit = ();
// `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);
// 使用されていない変数があると、コンパイラは警告を出します。
// 変数名の頭に`_`(アンダーバー)を付けると警告を消すことができます。
let _unused_variable = 3u32;
let noisy_unused_variable = 2u32;
// FIXME ^ 頭にアンダーバーを付けて、警告を抑えましょう。
// 警告はブラウザ上では表示されないかもしれません。
}
ミュータビリティ
変数はデフォルトでイミュータブル(変更不可能)ですがmut構文を使用することで変更可能になります。
fn main() {
let _immutable_binding = 1;
let mut mutable_binding = 1;
println!("Before mutation: {}", mutable_binding);
// 問題ありません。
mutable_binding += 1;
println!("After mutation: {}", mutable_binding);
// エラー!イミュータブルな変数に新しい値を代入することはできません。
_immutable_binding += 1;
}
コンパイラはミュータビリティに関するエラーの詳細を出してくれます。
スコープとシャドーイング
変数はスコープを持つため、 ブロック の中に閉じ込められています。ブロックとは{}で囲まれた領域のことです。
fn main() {
// この変数はmain関数の間、生存しています。
let long_lived_binding = 1;
// ここから下がmain関数より小さいスコープを持つブロックとなります。
{
// この変数はこのブロック内のみに存在します。
let short_lived_binding = 2;
println!("inner short: {}", short_lived_binding);
}
// ブロックの終わり
// エラー!`short_lived_binding`はこのスコープ内には存在しません。
println!("outer short: {}", short_lived_binding);
// FIXME ^ この行をコメントアウトしましょう
println!("outer long: {}", long_lived_binding);
}
変数のシャドーイングも可能です。
fn main() {
let shadowed_binding = 1;
{
println!("before being shadowed: {}", shadowed_binding);
// この変数はスコープ外の同名の変数を *シャドーイング* します。
let shadowed_binding = "abc";
println!("shadowed in inner block: {}", shadowed_binding);
}
println!("outside inner block: {}", shadowed_binding);
// この変数束縛は以前に定義した変数を *シャドーイング* します。
let shadowed_binding = 2;
println!("shadowed in outer block: {}", shadowed_binding);
}
前方宣言
It is possible to declare variable bindings first and initialize them later, but all variable bindings must be initialized before they are used: the compiler forbids use of uninitialized variable bindings, as it would lead to undefined behavior.
It is not common to declare a variable binding and initialize it later in the function. It is more difficult for a reader to find the initialization when initialization is separated from declaration. It is common to declare and initialize a variable binding near where the variable will be used.
fn main() {
// 変数を宣言。
let a_binding;
{
let x = 2;
// 変数を初期化。
a_binding = x * x;
}
println!("a binding: {}", a_binding);
let another_binding;
// エラー!初期化していない変数の使用
println!("another binding: {}", another_binding);
// FIXME ^ この行をコメントアウトしましょう
another_binding = 1;
println!("another binding: {}", another_binding);
}
値の凍結
データを同じ名前のイミュータブルな変数に束縛しなおすと、データは_凍結_されます。_凍結_したデータは、イミュータブルな束縛がスコープ外になるまで変更できません。
fn main() {
let mut _mutable_integer = 7i32;
{
// イミュータブルな`_mutable_integer`でシャドーイングします。
let _mutable_integer = _mutable_integer;
// エラー!`_mutable_integer`はこのスコープでは凍結しています。
_mutable_integer = 50;
// FIXME ^ この行をコメントアウトしましょう
// `_mutable_integer`はスコープを抜けます。
}
// OK!`_mutable_integer`はこのスコープでは凍結していません。
_mutable_integer = 3;
}
型
Rustには、基本データ型やユーザ定義型を定義したり変換したりする様々な方法があります。この章は以下の内容を扱います。
型キャスト
Rustは基本データ型について暗黙的な型変換(coerction)を行うことはありません。しかし明示的な型変換(casting)は可能です。その場合asキーワードを使用します。
整数型から整数型へ型変換する場合、C言語で可能なケースの場合はC言語と同じです。C言語で未定義の場合の挙動も、Rustでは完全に定義されています。
// Suppress all errors from casts which overflow.
#![allow(overflowing_literals)]
fn main() {
let decimal = 65.4321_f32;
// エラー!暗黙的な型変換はできません。
let integer: u8 = decimal;
// FIXME ^ この行をコメントアウトしましょう
// 明示的な型変換
let integer = decimal as u8;
let character = integer as char;
// エラー!変換ルールには制限があります。
// 浮動小数点数を文字に直接変換することはできません。
let character = decimal as char;
// FIXME ^ この行をコメントアウトしましょう
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 ONLY when the #![allow(overflowing_literals)]
// lint is specified like above. Otherwise there will be a compiler error.
// 1000はすでにu16に収まっているため変化しません。
println!("1000 as a u16 is: {}", 1000 as u16);
// 1000 - 256 - 256 - 256 = 232
// 詳しく見てみると、最下位ビットから8bitが保持され、
// 残りの上位ビットが切り取られる形になります。
println!("1000 as a u8 is : {}", 1000 as u8);
// -1 + 256 = 255
println!(" -1 as a u8 is : {}", (-1i8) as u8);
// 正の数では、これは剰余と同じです。
println!("1000 mod 256 is : {}", 1000 % 256);
// 符号付きの型にキャストする場合、(ビットとして見た)結果は対応する
// 符号無し型へのキャストを行った結果と同じです。
// 最上位ビットが1であれば、その値は負であることを示しています。
// すでに収まっている場合はそのままです。
println!(" 128 as a i16 is: {}", 128 as i16);
// 境界値のケースを考えると、128の8ビットにおける2の補数は -128です。
println!(" 128 as a i8 is : {}", 128 as i8);
// 上で示した例を繰り返すと
// 1000 as u8 -> 232
println!("1000 as a u8 is : {}", 1000 as u8);
// 232の8ビットにおける補数は -24。
println!(" 232 as a i8 is : {}", 232 as i8);
// Rust 1.45以降、浮動小数点数を整数にキャストするとき、
// `as`キーワードが *飽和的キャスト* を行います。
// 浮動小数点数の値が上限を超えたり下限を下回ったりする場合は、
// 戻り値は越えられた境界の値となります。
// 300.0 as u8 は 255
println!(" 300.0 as u8 is : {}", 300.0_f32 as u8);
// -100.0 as u8 は 0
println!("-100.0 as u8 is : {}", -100.0_f32 as u8);
// nan as u8 は 0
println!(" nan as u8 is : {}", f32::NAN as u8);
// この挙動は実行時にややコストがかかるため、安全でない方法で回避できます。
// ただし、結果はオーバーフローしたり *不正確な値* を返す場合があります。
// この方法は賢く使いましょう。
unsafe {
// 300.0 as u8 は 44
println!(" 300.0 as u8 is : {}", 300.0_f32.to_int_unchecked::<u8>());
// -100.0 as u8 は 156
println!("-100.0 as u8 is : {}", (-100.0_f32).to_int_unchecked::<u8>());
// nan as u8 は 0
println!(" nan as u8 is : {}", f32::NAN.to_int_unchecked::<u8>());
}
}
リテラル
数値型リテラルはサフィックスにより型を指定することが可能です。例えば、42というリテラルに対してi32型を指定するには42i32とします。
サフィックスを指定しない数値型リテラルの場合、その型がどのように使用されるかに依存して決められます。デフォルトでは整数型の場合i32が、浮動小数点数型にはf64が使われます。
fn main() {
// サフィックスを指定したリテラル。型は初期化とともに確定します。
let x = 1u8;
let y = 2u32;
let z = 3f32;
// サフィックスを指定しないリテラル。型は使用方法に依存します。
let i = 1;
let f = 1.0;
// `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は関数ですが、 絶対パス で呼び出されています。ソースコードは論理的に区切られた モジュール と呼ばれるものにわけられることができます。今回の場合はsize_of_val関数はmemモジュール内で定義されており、memモジュールはstdクレート 内で定義されています。より詳しくはモジュールとクレートを参照してください。
型推論
Rustの型推論エンジンはなかなか賢くできています。初期化の際に評価値の型をチェックするだけでなく、その後にどのような使われ方をしているかを見て推論します。以下がその例です。
fn main() {
// アノテーションのおかげで、コンパイラは`elem`がu8型であることがわかります。
let elem = 5u8;
// 空のベクタ(可変長の配列)を生成。
let mut vec = Vec::new();
// この時点でコンパイラは`vec`の型を知りませんが、
// 何らかの値のベクタ(`Vec<_>`)であるということだけは把握しています。
// `elem`をベクタに挿入。
vec.push(elem);
// よし!これでコンパイラは`vec`が`u8`のベクタ(`Vec<u8>`)
// であることを把握しました。
// TODO ^ 上の`vec.push(elem)`をコメントアウトしてみましょう。
println!("{:?}", vec);
}
このように、変数の型アノテーションは必要ありません。これでコンパイラもプログラマもハッピーですね!
エイリアス
type文を使用することで既存の型に新しい名前を付けることができます。その場合、名前はUpperCamelCaseでなくてはなりません。さもなくばコンパイラがエラーを出します。唯一の例外はusizeやf32のような基本データ型です。
// `NanoSecond` `Inch` `U64` を`u64`の別名として使用します。
type NanoSecond = u64;
type Inch = u64;
type U64 = u64;
fn main() {
// `NanoSecond` = `Inch` = `U64` = `u64`
let nanoseconds: NanoSecond = 5 as u64;
let inches: Inch = 2 as U64;
// 型のエイリアスは、元の型をより型安全する **わけではない** ことに注意。
// なぜならば、エイリアスは新たな型を定義している **わけではない** からです。
println!("{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
}
このようにエイリアスを付ける一番の理由はボイラープレートを減らすことです。例えばio::Result<T>型はResult<T, io::Error>の別名です。
参照
型変換
基本データ型同士はキャストを用いて変換できます。
Rustはカスタム型(例えばstructやenum)間の変換をトレイトを用いて行います。ジェネリックな型変換には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);
}
We can do something similar for defining a conversion for our own type.
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トレイトの逆の働きをし、ある型を他の型に変換する方法を定義します。
into() の呼び出し時にはほとんどの場合戻り値の型をコンパイラが決定できません。そのため戻り値の型は明示的に指定する必要があります。
use std::convert::Into;
#[derive(Debug)]
struct Number {
value: i32,
}
impl Into<Number> for i32 {
fn into(self) -> Number {
Number { value: self }
}
}
fn main() {
let int = 5;
// ここの型アノテーションを消してみましょう。
let num: Number = int.into();
println!("My number is {:?}", num);
}
From and Into are interchangeable
From と Into は相補的にデザインされているので両方のトレイトを実装する必要はありません。From トレイトを実装すれば、 Into は必要なときに呼ばれます。ただし、逆は真ではない点に注意してください。 Into が実装されていても、 From が自動的に提供されるわけではありません。
use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
// Define `From`
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let int = 5;
// use `Into`
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への型変換
To convert any type to a String is as simple as implementing the ToString trait for the type. Rather than doing so directly, you should implement the fmt::Display trait which automatically provides ToString and also allows printing the type as discussed in the section on 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関数を使用することですが、このときに型を推論できるようにするか、もしくはターボフィッシュ構文(::<>)を使用して型を指定するかのいずれかを行います。以下の例では、どちらの方法も紹介しています。
This will convert the string into the type specified as long as the FromStr trait is implemented for that type. This is implemented for numerous types within the standard library.
fn main() {
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
let sum = parsed + turbo_parsed;
println!("Sum: {:?}", sum);
}
To obtain this functionality on a user defined type simply implement the FromStr trait for that type.
use std::num::ParseIntError;
use std::str::FromStr;
#[derive(Debug)]
struct Circle {
radius: i32,
}
impl FromStr for Circle {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().parse() {
Ok(num) => Ok(Circle{ radius: num }),
Err(e) => Err(e),
}
}
}
fn main() {
let radius = " 3 ";
let circle: Circle = radius.parse().unwrap();
println!("{:?}", circle);
}
式
Rustのプログラムは(ほとんどの場合)文の連続でできています
fn main() {
// statement
// statement
// statement
}
文にはいくつかの種類があります。最も一般的なのは変数の束縛と;付きの式です。
fn main() {
// 変数束縛
let x = 5;
// 式;
x;
x + 1;
15;
}
コードブロックも式の一種です。よってブロックを丸ごと値として扱うことができます。その場合ブロック内の最後の式が場所を表す式(例えばローカル変数)に代入されます。ただし、ブロック内の最後の式が;で終わる場合は返り値は()になります。
fn main() {
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
// この式は`y`に代入されます。
x_cube + x_squared + x
};
let 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");
// この式は`i32`を返します。
10 * n
} else {
println!(", and is a big number, halve the number");
// ここでも返り値の型は`i32`でなくてはなりません。
n / 2
// TODO ^ セミコロン`;`をつけて返り値を返さないようにしてみましょう
};
// ^ ここにセミコロンを付けるのを忘れないように!
// `let`による変数束縛の際には必ず必要です。
println!("{} -> {}", n, big_n);
}
loop
Rustにはloopというキーワードが存在します。これは無限ループを作成するのに使用します。
ループから抜けだす時はbreak、即座に次のループに移るときはcontinueが使用できます。
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
// 無限ループ
loop {
count += 1;
if count == 3 {
println!("three");
// 残りの処理をスキップ。
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
// ループを抜ける。
break;
}
}
}
ネストとラベル
ネストしたループを回している時に外側のループをbreakまたはcontinueしたい場合があります。こういった場合には'labelを用いてループにラベルを貼り、break/continueにそのラベルを渡します。
#![allow(unreachable_code, unused_labels)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
// これは内側のループのみを中断します。
//break;
// こちらは外側を中断します。
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() {
// カウンタとなる変数
let mut n = 1;
// `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);
}
// カウンタに1を追加。
n += 1;
}
}
forループ
for と range
for in構文を用いることで、イテレータのそれぞれの要素に対して処理をすることが可能です。イテレータを作る最も単純な方法はa..bのような範囲記法です。これは「a」から「bのひとつ前」までの要素を順に生成します。
ではwhileの代わりにforを用いてFizzBuzzを書いてみましょう。
fn main() {
// `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`は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);
}
}
}
Just remember that even though you can compile the code when a>b, the loop gets never executed.
for i in 10..1{
println!("fizzbuzz");
}
If you want to count down, you need to use .rev() instead
for i in (1..10).rev(){
println!("fizzbuzz");
}
forとイテレータ
for in構文はイテレータとさまざまな方法でやり取りできます。Iteratorトレイトの章で説明したように、デフォルトではforループにおいてinto_iter関数がコレクションに対して適用されます。しかし、コレクションをイテレータに変換する方法はこれだけではありません。
into_iter、iter、iter_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),
}
}
// `names` has been 'moved' and can no longer be used.
// Try uncommenting the line below to see the compiler error:
// println!("names: {:?}", names);
}
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 ^ `number` の値を変えて試してみましょう。
println!("Tell me about {}", number);
match number {
// 単一の値とのマッチをチェック。
1 => println!("One!"),
// いくつかの値とのマッチをチェック。
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// TODO ^ 素数のリストに13を加えてみましょう。
// 特定の範囲の値とのマッチをチェック。
13..=19 => println!("A teen"),
// その他の場合の処理
_ => println!("Ain't special"),
// TODO ^ この全てをキャッチするアームをコメントアウトしてみましょう。
}
let boolean = true;
// マッチは式でもあります。
let binary = match boolean {
// マッチは全ての可能な値をカバーしなくてはなりません。
false => 0,
true => 1,
// TODO ^ 試しに片方をコメントアウトしてみましょう。
};
println!("{} -> {}", boolean, binary);
}
デストラクト
matchは値をさまざまなやり方でデストラクトすることができます。
参照
The Rust Reference for Destructuring
タプル
以下のように、タプルはmatchを用いてデストラクトすることができます。
fn main() {
let triple = (0, -2, 3);
// TODO ^ `triple`に別の値を入れてみましょう。
println!("Tell me about {:?}", triple);
// `match`を用いてタプルをデストラクトしてみましょう。
match triple {
// 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"),
(.., 2) => println!("last is `2` and the rest doesn't matter"),
(3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"),
// `..`を使うと、タプルの残りの部分を無視できます。
_ => println!("It doesn't matter what they are"),
// ここでは`_`は、値を変数に束縛しないことを意味します。
}
}
参照
配列とスライス
タプル同様、配列とスライスも以下のようにデストラクトできます。
fn main() {
// 配列中の値を変更してみましょう。または、スライスにしてみましょう。
let array = [1, -2, 6];
match array {
// 2番目と3番目の要素を変数に束縛します。
[0, second, third] =>
println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third),
// _で値を無視できます。
[1, _, third] => println!(
"array[0] = 1, array[2] = {} and array[1] was ignored",
third
),
// いくつかの値を束縛して残りを無視できます。
[-1, second, ..] => println!(
"array[0] = -1, array[1] = {} and all the other ones were ignored",
second
),
// 以下のコードはコンパイルできません。
// [-1, second] => ...
// 別の配列やスライスに値を持たせることもできます。
// (配列かスライスかは、マッチする値の型により異なります)
[3, second, tail @ ..] => println!(
"array[0] = 3, array[1] = {} and the other elements were {:?}",
second, tail
),
// 例えば、これらのパターンを組み合わせて、
// 最初と最後の値を束縛し、残りの値を配列に持たせることもできます。
[first, middle @ .., last] => println!(
"array[0] = {}, middle = {:?}, array[2] = {}",
first, middle, last
),
}
}
参照
列挙型
列挙型も似たやり方でデストラクトすることができます。
// `allow`は値を一つだけ使用したことによる警告を抑えるために存在します。
#[allow(dead_code)]
enum Color {
// これら3つの値は名前のみで扱うことができます。
Red,
Blue,
Green,
// 以下の値は名前と`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 ^ `color`に別の値を入れてみましょう。
println!("What color is it?");
// `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),
// 全ての値を列挙したのでその他の場合の処理は必要ありません。
}
}
参照
ポインタとref
Rustのポインタは、C/C++のポインタとは異なる概念なので、デストラクトとデリファレンスを同じようなやり方で扱うことはできません。
- デリファレンスには
*を用います。 - デストラクトには
&,ref,ref mutを用います。
fn main() {
// `i32`型への参照を代入します。
// `&`によって参照であることを明示しています。
let reference = &4;
match reference {
// 上で定義した`reference`という変数が`&val`とのパターンマッチ
// に用いられた場合、以下の2つの値が比較されていることになります。
// `&i32`
// `&val`
// ^ よって`&`を落とせば、`i32`が`val`に代入されることがわかります。
&val => println!("Got a value via destructuring: {:?}", val),
}
// `&`を使用したくない場合は、マッチングの前にデリファレンスします。
match *reference {
val => println!("Got a value via dereferencing: {:?}", val),
}
// いきなり参照を変数に代入するのではない場合はどうでしょう。
// 先ほどは右辺値が`&`で始まっていたので参照でしたが、
// これは違います。
let _not_a_reference = 3;
// このような場合、Rustでは変数束縛時に`ref`を宣言します。
// 要素の参照が作られて、それが束縛対象になります。
let ref _is_a_reference = 3;
// 同様にミュータブルな値の場合`ref mut`を使用することで参照を
// 取得できます。イミュータブルの場合と合わせてみていきましょう。
let value = 5;
let mut mut_value = 6;
// `ref`を使用して参照を作成。
match value {
ref r => println!("Got a reference to a value: {:?}", r),
}
// 同様に`ref mut`を使用。
match mut_value {
ref mut m => {
// 参照を取得、値を変更するためにはデリファレンスします。
*m += 10;
println!("We added 10. `mut_value`: {:?}", m);
},
}
}
参照
構造体
以下のようにして、構造体も同様にデストラクトすることができます。
fn main() {
struct Foo {
x: (u32, u32),
y: u32,
}
// 構造体の中の値を変えて、何が起きるか見てみましょう。
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),
// 構造体をデストラクトして変数をリネーム。
// 順番は重要ではありません。
Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
// 一部の変数を無視することもできます。
Foo { y, .. } => println!("y = {}, we don't care about x", y),
// `x`に言及していないため、以下はエラーになります。
//Foo { y } => println!("y = {}", y),
}
let faa = Foo { x: (1, 2), y: 3 };
// matchを使わずとも構造体はデストラクトできます。
let Foo { x : x0, y: y0 } = faa;
println!("Outside: x0 = {x0:?}, y0 = {y0}");
// ネストした構造体もデストラクトできます。
struct Bar {
foo: Foo,
}
let bar = Bar { foo: faa };
let Bar { foo: Foo { x: nested_x, y: nested_y } } = bar;
println!("Nested: nested_x = {nested_x:?}, nested_y = {nested_y:?}");
}
参照
ガード
match内の条件文をフィルタリングするために、 ガード を使用することができます。
#[allow(dead_code)]
enum Temperature {
Celsius(i32),
Fahrenheit(i32),
}
fn main() {
let temperature = Temperature::Celsius(35);
// ^ TODO `temperature`の値を変更してみましょう。
match temperature {
Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t),
// ^ `if`とそれに続く条件式がガードです。
Temperature::Celsius(t) => println!("{}C is equal to or below 30 Celsius", t),
Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t),
Temperature::Fahrenheit(t) => println!("{}F is equal to or below 86 Fahrenheit", t),
}
}
パターンが全てカバーされているかどうかを判断する際に、ガード条件は考慮されないことに注意してください。
fn main() {
let number: u8 = 4;
match number {
i if i == 0 => println!("Zero"),
i if i > 0 => println!("Greater than zero"),
// _ => unreachable!("Should never happen."),
// TODO ^ アンコメントしてコンパイルエラーを直してみましょう。
}
}
参照
束縛
いくつかの変数をまとめてマッチ対象とした場合、そのうちの一つを分岐先で使用することはそのままでは不可能です。match内では@マークを使用して変数を束縛することができます。
// `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?
// Could `match` n and use an `if` guard, but would
// not contribute to exhaustiveness checks.
// (Although in this case that would not matter since
// a "catch-all" pattern is present at the bottom)
// Instead, bind to `n` for the sequence of 1 ..= 12.
// Now the age can be reported.
n @ 1 ..= 12 => println!("I'm a child of age {:?}", n),
n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
// A similar binding can be done when matching several values.
n @ (1 | 7 | 15 | 13) => println!("I'm a teen of age {:?}", n),
// マッチしなかった場合の処理
n => println!("I'm an old person of age {:?}", n),
}
}
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.
// Could also use `Some(42)` and print `"The Answer: 42!"`
// but that would require changing `42` in 2 spots should
// you ever wish to change it.
// Could also use `Some(n) if n == 42` and print `"The Answer: {n}!"`
// but that would not contribute to exhaustiveness checks.
// (Although in this case that would not matter since
// the next arm is a "catch-all" pattern)
Some(n @ 42) => println!("The Answer: {}!", n),
// それ以外の数値にマッチ。
Some(n) => println!("Not interesting... {}", n),
// それ以外にマッチ(`None`の値)。
_ => (),
}
}
参照
if let
列挙型をマッチさせるとき、場合によってはmatchを使用すると不自然な書き方になってしまう場合があります。例えば
#![allow(unused)]
fn main() {
// `optional`という変数の型を`Option<i32>`に指定。
let optional = Some(7);
match optional {
Some(i) => println!("This is a really long string and `{:?}`", i),
_ => {},
// ^ `match`は全てのパターンに対して網羅的でなくてはならないので必要。
// 冗長に見えませんか?
};
}
この場合はif letを用いたほうが美しく、失敗時の処理も柔軟に行うことができます。
fn main() {
// 全て`Option<i32>`型
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
// `if let`文は以下と同じ意味。
// もしletがnumberをデストラクトした結果が`Some(i)`になるならば
// ブロック内(`{}`)を実行します。
if let Some(i) = number {
println!("Matched {:?}!", i);
}
// デストラクトした結果が`Some()`にならない場合の処理を明示したい場合、
// `else`を使用します。
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
// デストラクト失敗の場合。このブロック内を実行。
println!("Didn't match a number. Let's go with a letter!");
}
// デストラクト失敗時の処理を更に分岐させることもできます。
let i_like_letters = false;
if let Some(i) = emoticon {
println!("Matched {:?}!", i);
// デストラクト失敗。`else if`を評価し、処理をさらに分岐させます。
} else if i_like_letters {
println!("Didn't match a number. Let's go with a letter!");
} else {
// 今回は`else if`の評価がfalseなので、このブロック内がデフォルト。
println!("I don't like letters. Let's go with an emoticon :)!");
}
}
同様にif letは任意の列挙型のマッチに使えます。
// 列挙型の例
enum Foo {
Bar,
Baz,
Qux(u32)
}
fn main() {
// 変数の作成
let a = Foo::Bar;
let b = Foo::Baz;
let c = Foo::Qux(100);
// Foo::Barにマッチする変数
if let Foo::Bar = a {
println!("a is foobar");
}
// 変数bはFoo::Barにマッチしないので出力されません。
if let Foo::Bar = b {
println!("b is foobar");
}
// 変数cはFoo::Quxにマッチしつつ値を取り出せます。
// これはSome()と同様です。
if let Foo::Qux(value) = c {
println!("c is {}", value);
}
// 束縛も可能です。
if let Foo::Qux(value @ 100) = c {
println!("c is one hundred");
}
}
もう一つのメリットはif letがパラメータを持たない列挙型にも使えることです。列挙型がPartialEqを実装または導出していなくても問題ありません。その場合、列挙型のインスタンスは比較できないのでif Foo::Bar == aはコンパイルエラーとなりますが、if letは引き続き使えます。
試してみましょう。以下の例をif letを使って直してみてください。
// この列挙型はPartialEqを実装も導出もしていません。
// そのためFoo::Bar == aはエラーとなります。
enum Foo {Bar}
fn main() {
let a = Foo::Bar;
// Foo::Barにマッチする変数
if Foo::Bar == a {
// ^-- ここでコンパイルエラー。`if let`を使ってみましょう。
println!("a is foobar");
}
}
参照
let-else
🛈 Rust 1.65で安定化。
🛈 エディションを指定するには
rustc --edition=2021 main.rsのようにします。
let-elseを使うと反駁できるパターンにマッチさせつつ、通常のletのように変数束縛することができます。マッチしなかった場合は(break、return、panic!のように)処理を分岐させます。
use std::str::FromStr;
fn get_count_item(s: &str) -> (u64, &str) {
let mut it = s.split(' ');
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
(count, item)
}
fn main() {
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
}
束縛した変数名のスコープがmatchやif let-else式との主な違いです。matchやif let-elseでも似たようなことができますが、残念ながらコードの繰り返しや追加のletが必要になってしまいます。
#![allow(unused)]
fn main() {
use std::str::FromStr;
fn get_count_item(s: &str) -> (u64, &str) {
let mut it = s.split(' ');
let (count_str, item) = match (it.next(), it.next()) {
(Some(count_str), Some(item)) => (count_str, item),
_ => panic!("Can't segment count item pair: '{s}'"),
};
let count = if let Ok(count) = u64::from_str(count_str) {
count
} else {
panic!("Can't parse integer: '{count_str}'");
};
(count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
}
参照
Option, match, if let, let-else RFC.
while let
if letと同様に、while letも不格好なmatch処理を多少マシにしてくれます。例えば、以下のiをインクリメントする処理を見てください。
#![allow(unused)]
fn main() {
// `optional`という変数の型を`Option<i32>`に指定。
let mut optional = Some(0);
// 変数の照合を繰り返し行います。
loop {
match optional {
// もし`optional`のデストラクトに成功した場合、値に応じて処理を分岐。
Some(i) => {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ 3つものインデントが必要。
},
// デストラクトに失敗した場合、ループを脱出。
_ => { break; }
// ^ どうしてこんな行を書く必要が?もっと良い方法があるはずです!
}
}
}
while letを使ってすっきり書くことができます。
fn main() {
// `optional`という変数の型を`Option<i32>`に指定。
let mut optional = Some(0);
// これは次のように読めます。「`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);
}
// ^ インデントが少なく、デストラクト失敗時の処理を追加する必要がありません。
}
// ^ `if let`の場合は`else`/`else if`句が一つ余分にありましたが、
// `while let`では必要ありません。
}
参照
関数
関数はfnキーワードを用いて定義することができます。引数は変数と同様に型を指定する必要があり、もし関数が値を返すならば->の後にその型も指定する必要があります。
関数内の最後の式が返り値となります。関数の途中で値を返したい場合はreturn文を使用します。ループの最中やif文の中からも値を返すことができます。
では、今度は関数を使ってFizzBuzz問題を解いてみましょう!
// C/C++とは違い、関数の定義を行う順番に制限はありません。
fn main() {
// ここで関数を使用し、後ほど定義してもかまいません。
fizzbuzz_to(100);
}
// ブーリアン型を返す関数
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
// 例外的な引数を受けた場合、早めに返します。
if rhs == 0 {
return false;
}
// これは式であり、`return`キーワードは必要ありません。
lhs % rhs == 0
}
// 値を「返さない」関数、実際にはユニット型(`()`)を返しています。
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);
}
}
// 関数が`()`を返すとき、返り値の型は省略できます。
fn fizzbuzz_to(n: u32) {
for n in 1..=n {
fizzbuzz(n);
}
}
関連関数とメソッド
関数には特定の型に紐づいたものがあります。これには関連関数とメソッドの2つの形式があります。メソッドは特定のインスタンスに関連付けて呼ばれる関数であるのに対し、関連関数は型全体に対して定義される関数です。
struct Point {
x: f64,
y: f64,
}
// 実装のためのブロック。`Point`の持つ関連関数とメソッドを全て定義します。
impl Point {
// これは特定の型(すなわち Point)に関連した関数なので関連関数
//
// 関連関数はインスタンスからでなく呼び出すことができます。
// 以下のようにコンストラクタとしてよく使用されます。
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// 引数を2つ取る関連関数
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
struct Rectangle {
p1: Point,
p2: Point,
}
impl Rectangle {
// こちらはメソッド。`&self`は`self: &Self`の糖衣構文。
// `Self`は呼び出し元オブジェクトの型。この場合は`Rectangle`。
fn area(&self) -> f64 {
// `self`はドット演算子によって構造体のフィールドを参照できます。
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
// `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())
}
// このメソッドは呼び出し元オブジェクトがミュータブルであることを
// 必要とします。`&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`はヒープ上の整数を2つ保持します。
struct Pair(Box<i32>, Box<i32>);
impl Pair {
// このメソッドは呼び出し元オブジェクトを「消費」します。
// `self`は`self: Self`の糖衣構文です。
fn destroy(self) {
// `self`をデストラクト。
let Pair(first, second) = self;
println!("Destroying Pair({}, {})", first, second);
// `first`、`second`はスコープから抜け出すと同時に、解放されます。
}
}
fn main() {
let rectangle = Rectangle {
// 関連関数はコロンを2つ挟んで呼び出すことができます。
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
// メソッドはドット演算子を用いて呼び出されます。
// 最初の引数`&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),
};
// エラー!`rectangle`はイミュータブルですがこのメソッドは
// ミュータブルなオブジェクトを必要とします。
//rectangle.translate(1.0, 0.0);
// TODO ^ この行をアンコメントしてみましょう。
// OK!ミュータブルなオブジェクトはミュータブルなメソッドを呼び出せます。
square.translate(1.0, 1.0);
let pair = Pair(Box::new(1), Box::new(2));
pair.destroy();
// エラー!先ほどの`destroy`で`pair`はすでに消費されてしまっています。
//pair.destroy();
// TODO ^ この行をアンコメントしてみましょう。
}
クロージャ
Rustにおけるクロージャは、その外側の環境を捕捉した関数のことです。例えば、次のコードは変数xを捕捉したクロージャです。
|val| val + x
クロージャの構文や機能は、その場限りの用途で何かを作るのに便利です。クロージャの呼び出しは関数の呼び出しと全く同じです。しかし、入力の型と戻り値の型は推論させることができますが、入力変数の名前は必ず指定しなくてはなりません。
クロージャの他の特徴を以下に示します。
- 入力変数を囲むのに、
()の代わりに||を用います。 - 本体が単一の式の場合は、本体の区切り文字(
{})を省略できます。(それ以外の場合は必須です) - 外側の環境にある変数を捕捉することができます。
fn main() {
let outer_var = 42;
// 通常の関数は周辺の環境の変数を参照できません。
//fn function(i: i32) -> i32 { i + outer_var }
// TODO: 上の行をアンコメントしてコンパイルエラーを見てみましょう。
// 代わりにクロージャを定義することをコンパイラが提案してくれます。
// クロージャは匿名なので、参照に束縛して使います。
// 型アノテーションは、通常の関数と同様の方法で行えますが、必須ではありません。
// `{}`も必須ではありません。
// このように定義した無名関数を適切な名前の変数に代入します。
let closure_annotated = |i: i32| -> i32 { i + outer_var };
let closure_inferred = |i | i + outer_var ;
// クロージャを呼び出す。
println!("closure_annotated: {}", closure_annotated(1));
println!("closure_inferred: {}", closure_inferred(1));
// クロージャの型が一度推論されると、別の型にあらためて推論することはできません。
//println!("cannot reuse closure_inferred with another type: {}", closure_inferred(42i64));
// TODO: 上の行をアンコメントしてコンパイルエラーを見てみましょう。
// 引数なしで`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.
let print = || println!("`color`: {}", color);
// 借用を行ったクロージャを呼び出します。
print();
// `color` can be borrowed immutably again, because the closure only holds
// an immutable reference to `color`.
let _reborrow = &color;
print();
// 最後に`print`を使用した後は移動や再借用が許可されます。
let _color_moved = color;
let mut count = 0;
// `count`をインクリメントするためのクロージャ。`count`と`&mut count`
// の両方を取ることができますが、後者のほうが制限が少ないため、
// (訳注: `count`だと`&mut count`と違い、一度しか呼ぶことができない。)
// そちらを取ります。直後に`count`を借用します。
//
// `inc`には`mut`をつける必要があります。なぜならミュータブルな型が
// 中で使用されているからです。ミュータブルなクロージャは呼ぶたびに
// 内部変数を変更します。
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
// ミュータブルな借用を使ってクロージャを実行。
inc();
// The closure still mutably borrows `count` because it is called later.
// An attempt to reborrow will lead to an error.
// let _reborrow = &count;
// ^ TODO: try uncommenting this line.
inc();
// クロージャはもう`&mut count`を借用する必要がありません。
// なので、エラーを起こさず再借用することができます。
let _count_reborrowed = &mut count;
// コピー不可能な型
let movable = Box::new(3);
// `mem::drop`は`T`(ジェネリック型)を取る必要があるため、このクロージャは
// 参照ではなく値を取ります。その場合、もしもコピー可能な値ならば、
// 元の値はそのままでコピーのみを取ります。
// 不可能ならば値そのものを移動させます。
let consume = || {
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
// `consume`は変数を消費(開放)するため、一度しか呼び出すことができません。
consume();
// consume();
// ^ TODO: この行のコメントアウトを解除しましょう。
}
バーティカルパイプ(訳注:縦線記号||)の前にmoveを使用することで、キャプチャする変数の所有権を取ることをクロージャに強制します。
fn main() {
// `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());
// ^ 上の行のコメントアウトを解除すると、コンパイル時エラーになります。
// これは変数の所有権が移った後の再利用を借用チェッカーが許可しないからです。
// クロージャのシグネチャから`move`を削除すると、クロージャは
// _haystack_ 変数をイミュータブルで借用するようになります。
// そのため _haystack_ はまだ利用可能となり、上の行のコメントアウトを
// 解除してもエラーが発生しなくなります。
}
参照
捕捉時の型推論
Rustはたいていの場合、型アノテーションなしでも変数を捕捉する方法を臨機応変に選択してくれますが、関数を書く場合にはこの曖昧さは許されません。引数のパラメータとしてクロージャを取る場合、そのクロージャの完全な型はいくつかのtraitsの中の1つを使って明示されなければなりません。どれが使われるかは、捕捉された値でクロージャが何をするかによって決まります。制限の少ない順に並べると、下記の通りです。
Fn:参照(&T)によって捕捉するクロージャFnMut:ミュータブルな参照(&mut T)によって捕捉するクロージャFnOnce:値(T)によって捕捉するクロージャ
変数ごとに、コンパイラは可能な限り制約の少ない方法でその変数を捕捉します。
例えば、FnOnceというアノテーションの付けられたパラメータを考えてみましょう。これはそのクロージャが&T、&mut TもしくはTの どれか で捕捉することを指定するものですが、コンパイラは捕捉した変数がそのクロージャの中でどのように使用されるかに基づき、最終的に捕捉する方法を選択することになります。
これは、もし移動が可能であれば、いずれの種類の借用であっても同様に可能だからです。その逆は正しくないことに注意してください。パラメータがFnとしてアノテーションされている場合、変数を&mut TやTで捕捉することは許可されません。しかし&Tは許可されます。
以下の例で、Fn、FnMut、およびFnOnceを入れ替えて、何が起こるのかを見てみましょう。
// クロージャを引数として取り、クロージャを呼び出す関数。
// <F>はFが「ジェネリックな型パラメータ」であることを示します。
fn apply<F>(f: F) where
// クロージャには引数も返り値もありません。
F: FnOnce() {
// ^ TODO: ここを`Fn`あるいは`FnMut`に変えてみましょう。
f();
}
// クロージャを引数に取り、`i32`を返す関数
fn apply_to_3<F>(f: F) -> i32 where
// このクロージャは引数、返り値ともに`i32`。
F: Fn(i32) -> i32 {
f(3)
}
fn main() {
use std::mem;
let greeting = "hello";
// コピーではなくmoveが起きる型
let mut farewell = "goodbye".to_owned();
// 変数を2つ捕捉。`greeting`は参照を、
// `farewell`は値をそれぞれ捕捉します。
let diary = || {
// `greeting`は参照なので、`Fn`が必要です。
println!("I said {}.", greeting);
// `farewell`の値を変更するので、この時点で`FnMut`
// が必要になります。
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");
// `mem::drop`を明示的に呼ぶと`farewell`が値で
// 捕捉される必要性が発生します。よって`FnOnce`が必要になります。
mem::drop(farewell);
};
// クロージャを適用する関数を実行。
apply(diary);
// `double`は`apply_to_3`のトレイト境界を満たす。
let double = |x| 2 * x;
println!("3 doubled: {}", apply_to_3(double));
}
参照
std::mem::drop, Fn, FnMut, ジェネリクス, where, FnOnce
匿名型
クロージャが周辺の環境から変数を取得するやり方は非常に明瞭です。何か注意すべき点はあるのでしょうか?もちろんです。関数内でクロージャを使う場合、ジェネリクスを使用する必要があります。詳しく見ていきましょう。
#![allow(unused)]
fn main() {
// `F` はジェネリック型でなくてはなりません。
fn apply<F>(f: F) where
F: FnOnce() {
f();
}
}
クロージャが定義されると、コンパイラは裏側で、無名の構造体を作り、そこにクロージャによって使用される外側の変数を入れます。同時にFn、FnMut、FnOnceという名のトレイトのいずれか一つを介してこの構造体に関数としての機能を実装し、実際に呼び出されるまで待ちます。
この無名構造体は型が未指定なため、関数を実行する際にはジェネリクスが必要とされます。とはいえ、<T>で指定するだけでは、まだ曖昧です。(訳注:&self、&mut self、selfのいずれをとるのかがわからないため)そのため、Fn、FnMut、FnOnceのいずれか一つを実装することで対応しています。
// `F`は引数と戻り値を持たないクロージャ`Fn`を実装していなくてはなりません。
// これはまさに`print`に必要とされるものです。
fn apply<F>(f: F) where
F: Fn() {
f();
}
fn main() {
let x = 7;
// `x`を無名の構造体に入れ、それに対し`Fn`を実装します。
// (訳注: ここでは`Fn`は`fn Fn(&self) -> {println!("{}", &self)}`)
// その構造体を`print`に代入します。
let print = || println!("{}", x);
apply(print);
}
参照
関数を受け取る関数
これまで、クロージャを引数として渡せることを見てきました。すると次の疑問が浮かんできます。「クロージャではない普通の関数を引数として渡すことは可能なのだろうか?」可能です!もしパラメータとしてクロージャを取る関数を定義すれば、そのクロージャのトレイト境界を満たす任意の関数をパラメータとして渡すことができます。
// 関数を引数として取り、即座に実行する関数を定義。
fn call_me<F: Fn()>(f: F) {
f();
}
// `Fn`境界を満たすラッパ関数を定義。
fn function() {
println!("I'm a function!");
}
fn main() {
// `Fn`境界を満たすクロージャを定義。
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}
クロージャによる変数の捕捉がどのように行われているかを詳しく見たいときはFn、FnMut、FnOnceを参照してください。
参照
クロージャを返す関数
クロージャを引数のパラメータとして用いることができるのと同様に、クロージャを戻り値として返すことも可能です。しかし無名のクロージャの型はその定義上、不明であるため、クロージャを返すためにはimpl Traitを使用する必要があります。
クロージャを返すために有効なトレイトは下記の通りです。
FnFnMutFnOnce
更に、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 {
// イテレートされる値の型
type Item;
// `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` is the closure parameter type,
// which is determined by the iterator (e.g., `&T` for `.iter()`,
// `T` for `.into_iter()`).
F: FnMut(Self::Item) -> bool;
}
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// ベクタ型に対する`iter()`は`&i32`を生成するので、`i32`へとデストラクト。
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
// `into_iter()`の場合は`i32`を生成するので、デストラクトは必要ありません。
println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2));
// `iter()`は`vec1`とその要素を借用するだけなので、再び利用できます。
println!("vec1 len: {}", vec1.len());
println!("First element of vec1 is: {}", vec1[0]);
// `into_iter()`は`vec2`とその要素をムーブするので、再利用できません。
// println!("First element of vec2 is: {}", vec2[0]);
// println!("vec2 len: {}", vec2.len());
// TODO: 上の2行をアンコメントしてコンパイルエラーを確認しましょう。
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// 配列に対する`iter()`は`&i32`を生成します。
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
// 配列に`into_iter()`を使うと`&i32`を生成します。
println!("2 in array2: {}", array2.into_iter().any(|x| x == 2));
}
参照
イテレータによる検索
Iterator::findはイテレータを辿る関数で、条件を満たす最初の値を探します。もし条件を満たす値がなければNoneを返します。型シグネチャは以下のようになります。
pub trait Iterator {
// イテレートされる値の型
type Item;
// `find`は`&mut self`を取るため、イテレータを呼び出した値を借用し
// 変更しますが、消費することはありません。
fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where
// `FnMut`はクロージャによって捕捉される変数が変更される
// 事はあっても消費されることはないということを示します。
// `&Self::Item`はクロージャが変数を参照として取ることを示します。
P: FnMut(&Self::Item) -> bool;
}
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `vec1.iter()` yields `&i32`.
let mut iter = vec1.iter();
// `vec2.into_iter()` yields `i32`.
let mut into_iter = vec2.into_iter();
// `iter()` yields `&i32`, and `find` passes `&Item` to the predicate.
// Since `Item = &i32`, the closure argument has type `&&i32`,
// which we pattern-match to dereference down to `i32`.
println!("Find 2 in vec1: {:?}", iter.find(|&&x| x == 2));
// `into_iter()` yields `i32`, and `find` passes `&Item` to the predicate.
// Since `Item = i32`, the closure argument has type `&i32`,
// which we pattern-match to dereference down to `i32`.
println!("Find 2 in vec2: {:?}", into_iter.find(|&x| x == 2));
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `array1.iter()` yields `&i32`, and `find` passes `&Item` to the
// predicate. Since `Item = &i32`, the closure argument has type `&&i32`.
println!("Find 2 in array1: {:?}", array1.iter().find(|&&x| x == 2));
// `array2.into_iter()` yields `i32` (since Rust 2021 edition), and
// `find` passes `&Item` to the predicate. Since `Item = i32`, the
// closure argument has type `&i32`.
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];
// `position` passes the iterator’s `Item` by value to the predicate.
// `vec.iter()` yields `&i32`, so the predicate receives `&i32`,
// which we pattern-match to dereference to `i32`.
let index_of_first_even_number = vec.iter().position(|&x| x % 2 == 0);
assert_eq!(index_of_first_even_number, Some(5));
// `vec.into_iter()` yields `i32`, so the predicate receives `i32` directly.
let index_of_first_negative_number = vec.into_iter().position(|x| x < 0);
assert_eq!(index_of_first_negative_number, None);
}
参照
std::iter::Iterator::rposition
高階関数
Rustには高階関数(Higher Order Functions, HOF)を扱う機能が備わっています。
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
fn main() {
println!("Find the sum of all the numbers with odd squares under 1000");
let upper = 1000;
// 宣言型プログラミングによるアプローチ
// 値を蓄積する変数を宣言。
let mut acc = 0;
// 0から無限までイテレートします。
for n in 0.. {
// 値を2乗。
let n_squared = n * n;
if n_squared >= upper {
// 上限に達した場合、ループを終了。
break;
} else if is_odd(n_squared) {
// 奇数ならば値を値を足しあわせていきます。
acc += n;
}
}
println!("imperative style: {}", acc);
// 関数型プログラミングによるアプローチ
let sum: u32 =
(0..).take_while(|&n| n * n < upper) // 上限より小さい値で
.filter(|&n| is_odd(n * n)) // かつ奇数のものを
.sum(); // 合計します。
println!("functional style: {}", sum);
}
Option と イテレータ には高階関数が使用されています。
発散する関数
発散する関数は決してリターンしない関数です。こうした関数は ! を使って、空の型であることが示されます。
#![allow(unused)]
fn main() {
fn foo() -> ! {
panic!("This call never returns.");
}
}
他の全ての型と異なり、この型はインスタンス化できません。この型が持ちうる全ての値の集合は空です。この型は()型とは異なることに注意してください。()型は値をただ1つだけ持つ型です。
例えば、この関数は通常どおりリターンしますが、戻り値には何の情報も含みません。
fn some_fn() {
()
}
fn main() {
let _a: () = some_fn();
println!("This function returns and you can see this line.");
}
一方、この関数は呼び出し元に決してリターンしません。
#![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 actually very useful and often handy. The main advantage of this type is that it can be cast to any other type, making it versatile in situations where an exact type is required, such as in match branches. This flexibility 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 {
// 変数"addition"の型がu32であるため、
// このmatch式はu32をリターンしなければならないことに注意。
let addition: u32 = match i%2 == 1 {
// 変数"i"はu32型であるため、全く問題ありません。
true => i,
// 一方、"continue"式はu32をリターンしませんが、これでも問題ありません。
// 決してリターンしないため、このmatch式が要求する型に違反しないからです。
false => continue,
};
acc += addition;
}
acc
}
println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
}
この型は、ネットワークサーバのような永遠にループする関数(例:loop {})の戻り値の型や、プロセスを終了させる関数(例:exit())の戻り値の型としても使用されます。
モジュール
Rustにはコードを階層的に分割し、お互いの機能を隠蔽・公開するための強力なモジュールシステムがあります。
モジュールは関数、構造体、トレイト、implブロック、さらには他のモジュールなどの要素の集合です。
可視性
デフォルトでは、モジュール内の要素はプライベートですが、これはpubで修飾することでパブリックな属性にすることができます。パブリックな属性のみがモジュールの外のスコープからアクセスできます。
// `my_mod`という名前のモジュール
mod my_mod {
// モジュール内の要素はデフォルトでプライベート。
fn private_function() {
println!("called `my_mod::private_function()`");
}
// `pub`を用いてパブリックに変更。
pub fn function() {
println!("called `my_mod::function()`");
}
// モジュール内からならば、プライベートな属性にアクセスできます。
pub fn indirect_access() {
print!("called `my_mod::indirect_access()`, that\n> ");
private_function();
}
// モジュールもネストできます。
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()`");
}
// `pub(in path)`形式で宣言された関数は該当のパス内でのみアクセスできます。
// `path`は親や先祖のモジュールでなくてはなりません。
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();
}
// `pub(self)`形式で宣言された関数は現在のモジュール内でのみアクセスできます。
// つまり、プライベートにするのと同じです。
pub(self) fn public_function_in_nested() {
println!("called `my_mod::nested::public_function_in_nested()`");
}
// `pub(super)`形式で宣言された関数は親モジュール内でのみアクセスできます。
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)により関数は現在のクレート内でのみアクセスできます。
pub(crate) fn public_function_in_crate() {
println!("called `my_mod::public_function_in_crate()`");
}
// ネストしたモジュールも、同様の性質を示します。
mod private_nested {
#[allow(dead_code)]
pub fn function() {
println!("called `my_mod::private_nested::function()`");
}
// 親がプライベートな場合、子要素がより大きなスコープでアクセスできるように宣言されていても、
// 子要素にアクセス可能な範囲は制限されます。
#[allow(dead_code)]
pub(crate) fn restricted_function() {
println!("called `my_mod::private_nested::restricted_function()`");
}
}
}
fn function() {
println!("called `function()`");
}
fn main() {
// モジュールによって、同名の関数を区別することができます。
function();
my_mod::function();
// パブリックな要素ならば、たとえネストしたものでも、
// モジュールの外からアクセスすることができます。
my_mod::indirect_access();
my_mod::nested::function();
my_mod::call_public_function_in_my_mod();
// pub(crate)の要素は同じクレートのどこからでも呼び出すことができます。
my_mod::public_function_in_crate();
// pub(in path)の要素は指定されたモジュールからのみ呼び出すことができます。
// エラー!`public_function_in_my_mod`関数はプライベート。
//my_mod::nested::public_function_in_my_mod();
// TODO ^ 試しにこの行をアンコメントしてみましょう。
// プライベートな要素は、たとえパブリックなモジュール内に存在していても
// 直接アクセスすることはできません。
// エラー!`private_function`はプライベート。
//my_mod::private_function();
// TODO ^ 試しにこの行をアンコメントしてみましょう。
// エラー!`private_function`はプライベート。
//my_mod::nested::private_function();
// TODO ^ 試しにこの行をアンコメントしてみましょう。
// エラー!`private_nested`はプライベートなモジュール。
//my_mod::private_nested::function();
// TODO ^ 試しにこの行をアンコメントしてみましょう。
// エラー!`private_nested`はプライベートなモジュール。
//my_mod::private_nested::restricted_function();
// TODO ^ 試しにこの行をアンコメントしてみましょう。
}
構造体の場合
構造体はそれ自身に加え、フィールドごとにもパブリック・プライベートを設定することができます。デフォルトではプライベートですが、pub宣言をすることで、フィールドをパブリックにすることができます。これは、構造体がモジュールの外から参照される時に限り意味のあるもので、情報の隠蔽(カプセル化)を達成するための機能です。
mod my {
// パブリックなフィールド`T`(ジェネリック型)を持つパブリックな構造体
pub struct OpenBox<T> {
pub contents: T,
}
// プライベートなフィールド`T`(ジェネリック型)を持つパブリックな構造体
pub struct ClosedBox<T> {
contents: T,
}
impl<T> ClosedBox<T> {
// パブリックなコンストラクタメソッドを持つ構造体
pub fn new(contents: T) -> ClosedBox<T> {
ClosedBox {
contents: contents,
}
}
}
}
fn main() {
// パブリックなフィールドを持つパブリックな構造体は、通常通り
// インスタンス化できます。
let open_box = my::OpenBox { contents: "public information" };
// フィールドにも普通にアクセスできます。
println!("The open box contains: {}", open_box.contents);
// プライベートなフィールドを持つ構造体は、インスタンス化する際に
// フィールド名を指定することができません。
// エラー!`ClosedBox`にはプライベートなフィールドがあります。
//let closed_box = my::ClosedBox { contents: "classified information" };
// TODO ^ 試しにここをアンコメントしてみましょう。
// そのような場合でも、パブリックなコンストラクタを介して作成
// することはできます。
let _closed_box = my::ClosedBox::new("classified information");
// パブリックな構造体でも、プライベートなフィールドにはアクセスできません。
// エラー!`contents`フィールドはプライベートです。
//println!("The closed box contains: {}", _closed_box.contents);
// TODO ^ ここをアンコメントしてみましょう。
}
参照
use宣言
use宣言をすることで、要素の絶対パスを新しい名前に束縛することができ、より簡潔な記述が可能になります。例えば以下のように使えます。
use crate::deeply::nested::{
my_first_function,
my_second_function,
AndATraitType
};
fn main() {
my_first_function();
}
asキーワードを使用することで、インポートを別名に束縛することができます。
// `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() {
// `deeply::nested::function`へ、より簡潔にアクセス。
other_function();
println!("Entering block");
{
// これは`use deeply::nested::function as function`と同等
// この`function()`は外の`function()`をシャドウイングします。
use crate::deeply::nested::function;
// `use`束縛は局所的なスコープを持ちます。
// この場合には`function()`のシャドーイングはこのブロック内のみとなります。
function();
println!("Leaving block");
}
function();
}
You can also use pub use to re-export an item from a module, so it can be accessed through the module’s public interface:
mod deeply {
pub mod nested {
pub fn function() {
println!("called `deeply::nested::function()`");
}
}
}
mod cool {
pub use crate::deeply::nested::function;
}
fn main() {
cool::function();
}
superとself
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() {
// `function`という名の様々な関数をこのスコープ内から参照してみましょう。
print!("called `my::indirect_call()`, that\n> ");
// `self`キーワードは現在のモジュールスコープを示します。
// この場合は`my`です。`self::function()`と`funcition()`は
// 同じ関数であるため、同じ結果になります。
self::function();
function();
// `my`以下の別のモジュールを呼び出す際に
// `self`を用いて明示的に参照できます。
self::cool::function();
// `super`は親スコープ(`my`の外側)を参照します。
super::function();
// 以下は *クレート* スコープ内の`cool::function`を束縛します。
// この場合、クレートスコープは一番外側のスコープです。
{
use crate::cool::function as root_function;
root_function();
}
}
}
fn main() {
my::indirect_call();
}
ファイルの階層構造
モジュールはファイル・ディレクトリの階層構造と対応関係にあります。以下の様なファイルで可視性の例を詳しく見ていきましょう。
$ tree .
.
├── my
│ ├── inaccessible.rs
│ └── nested.rs
├── my.rs
└── split.rs
split.rsは以下のようになります。
// このように宣言すると、`my.rs`という名のファイルを探し、
// その内容をこのファイル中で`my`という名から使用することができるようにします。
mod my;
fn function() {
println!("called `function()`");
}
fn main() {
my::function();
function();
my::indirect_access();
my::nested::function();
}
my.rsは以下のようになります。
// 同様に`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();
}
my/nested.rsは以下のようになります。
pub fn function() {
println!("called `my::nested::function()`");
}
#[allow(dead_code)]
fn private_function() {
println!("called `my::nested::private_function()`");
}
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.rsがmod宣言を含んでいるのならば、コンパイルの 前に 、そのモジュールファイルの中身がmodの位置に挿入されます。言い換えると、それぞれのモジュールが独立にコンパイルされるということはありませんが、それぞれのクレートは互いに独立にコンパイルされるということです。
クレートはバイナリあるいはライブラリ形式でコンパイルされることが可能です。デフォルトではrustcはクレートからバイナリを作り出しますが、この振る舞いは--crate-typeフラグにlibを渡すことでオーバーライドできます。
ライブラリの作成
ではライブラリを作成し、それを別のクレートにリンクする方法を見ていきましょう。
rary.rsは以下のようになります。
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アトリビュートを用いてオーバーライドできます。
ライブラリの利用
クレートをこの新しいライブラリにリンクするには、rustcの--externフラグを利用します。クレートの要素を全てライブラリと同じ名前のモジュールにインポートします。一般に、このモジュールは他のモジュールと同じように振る舞います。
// extern crate rary; // Rust 2015以前で必要。
fn main() {
rary::public_function();
// エラー!`private_function`はプライベート。
//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 && ./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のプロジェクトを新しく作るには下記のようにします。
# バイナリ
cargo new foo
# ライブラリ
cargo new --lib bar
この章の残りでは、ライブラリではなくバイナリを作ることを想定しますが、コンセプトはすべて同じです。
上のコマンドを実行すると、次のようなファイル階層ができます。
.
├── bar
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── foo
├── Cargo.toml
└── src
└── main.rs
main.rsがこの新規プロジェクト foo のルートのソースファイルです。なにも新しいことはありませんね。Cargo.tomlはこのプロジェクトの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
rand = { git = "https://github.com/rust-lang-nursery/rand" } # from online repo
bar = { path = "../bar" } # from a path in the local filesystem
cargoは依存管理ツール以上のこともできます。Cargo.tomlのformat 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内の各ファイルは個別の統合テストです。これはライブラリを依存クレートから呼ばれたかのようにテストできます。
テストの章は3つの異なるテストスタイルについて解説しています。単体テスト、ドキュメンテーションテスト、そして結合テストです。
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 4 tests
test test_bar ... ok
test test_baz ... ok
test test_foo_bar ... ok
test test_foo ... ok
test result: ok. 4 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は複数のテストを並列で実行することがありますので、それらが互いに競合しないようにしてください。
並行性が問題を引き起こす一例として、以下のように、2つのテストが1つのファイルに出力するケースがあります。
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
// 必要なモジュールをインポートします。
use std::fs::OpenOptions;
use std::io::Write;
// ファイルに書き込むテスト
#[test]
fn test_file() {
// ferris.txtというファイルを開くか、存在しない場合は作成します。
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("ferris.txt")
.expect("Failed to open ferris.txt");
// "Ferris"と5回書き込みます。
for _ in 0..5 {
file.write_all("Ferris\n".as_bytes())
.expect("Could not write to ferris.txt");
}
}
// 同じファイルに書き込むテスト
#[test]
fn test_file_also() {
// ferris.txtというファイルを開くか、存在しない場合は作成します。
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open("ferris.txt")
.expect("Failed to open ferris.txt");
// "Corro"と5回書き込みます。
for _ in 0..5 {
file.write_all("Corro\n".as_bytes())
.expect("Could not write to ferris.txt");
}
}
}
}
以下のような結果を得ようと意図しています。
$ cat ferris.txt
Ferris
Ferris
Ferris
Ferris
Ferris
Corro
Corro
Corro
Corro
Corro
しかし、実際にferris.txtに出力されるのは、以下の通りです。
$ cargo test test_file && cat ferris.txt
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を参照してください。
アトリビュート
アトリビュートはモジュール、クレート、要素に対するメタデータです。以下がその使用目的です。
- コンパイル時の条件分岐
- クレート名、バージョン、種類(バイナリか、ライブラリか)の設定
- リントの無効化
- コンパイラ付属の機能(マクロ、グロブ、インポートなど)の使用
- 外部ライブラリへのリンク
- ユニットテスト用の関数を明示
- ベンチマーク用の関数を明示
- アトリビュートマクロ
Attributes look like #[outer_attribute] or #![inner_attribute], with the difference between them being where they apply.
-
#[outer_attribute]applies to the item immediately following it. Some examples of items are: a function, a module declaration, a constant, a structure, an enum. Here is an example where attribute#[derive(Debug)]applies to the structRectangle:#![allow(unused)] fn main() { #[derive(Debug)] struct Rectangle { width: u32, height: u32, } } -
#![inner_attribute]applies to the enclosing item (typically a module or a crate). In other words, this attribute is interpreted as applying to the entire scope in which it’s placed. Here is an example where#![allow(unused_variables)]applies to the whole crate (if placed inmain.rs):#![allow(unused_variables)] fn main() { let x = 3; // This would normally warn about an unused variable. }
アトリビュートは以下の様な書き方で引数を取ることができます。
#[attribute = "value"]#[attribute(key = "value")]#[attribute(value)]
アトリビュートは複数の値を取ることができ、複数の行に分割することもできます。
#[attribute(value, value2)]
#[attribute(value, value2, value3,
value4, value5)]
dead_code
コンパイラはdead_codeと呼ばれるリント機能を持つため、使用されていない関数が存在するときに警告を出します。アトリビュート によってこの機能を無効化することができます。
fn used_function() {}
/ `#[allow(dead_code)]`は`dead_code`リントを抑制するアトリビュートです。
#[allow(dead_code)]
fn unused_function() {}
fn noisy_unused_function() {}
// FIXME ^ 警告を抑制するアトリビュートを追加しましょう。
fn main() {
used_function();
}
実際のコード中では、使用されていないコードが有る場合はそれを除外するべきです。この文書中では随所でアトリビュートによって警告を抑制していますが、それはあくまでインタラクティブな例を皆さんに提供するためです。
クレート
crate_typeアトリビュートは、そのクレートがライブラリ、バイナリのいずれにコンパイルされるべきかをコンパイラに伝えるために使用します。ライブラリの場合は、どのタイプのライブラリであるかも伝えることができます。crate_nameはクレートの名前を決定するのに使用します。
しかし、crate_typeアトリビュートもcrate_nameアトリビュートも、RustのパッケージマネージャCargoを利用している場合は何の影響もないと知っておくことは重要です。Cargoは大半のRustプロジェクトで利用されており、実世界でのcrate_typeとcrate_nameの利用は比較的限られています。
// このクレートはライブラリです。
#![crate_type = "lib"]
// このライブラリの名前は「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リテラルに評価され実行時にチェックすることが可能です。いずれの場合も適切なシンタックスで記述する必要があります。
#[cfg]と異なり、cfg!はコードを削除せず、trueまたはfalseに評価されるだけです。例えば、cfg!が何を評価しているかに関係なく、cfg!が条件に利用されるとき、if/else式の中のすべてのブロックが有効でなくてはなりません。
// この関数はターゲットOSがLinuxの時のみコンパイルされます。
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
println!("You are running 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!");
}
}
参照
条件の追加
target_osのように、いくつかの条件分岐はrustcが暗黙のうちに提供しています。条件を独自に追加する場合には--cfgフラグを用いてrustcに伝える必要があります。
#[cfg(some_condition)]
fn conditional_function() {
println!("condition met!");
}
fn main() {
conditional_function();
}
独自のcfgフラグを用いない場合、何が起きるかやってみてください。
cfgフラグがある場合:
$ rustc --cfg some_condition custom.rs && ./custom
condition met!
ジェネリクス
ジェネリクスとは、型と関数の機能をより汎用的に使えるようにするための機能です。これはあらゆる局面でコードの重複を避けるために非常に役立ちますが、多少構文が複雑になります。すなわち、ジェネリック型を使いこなすには、どのようなジェネリック型がきちんと機能するかに細心の注意を払う必要があります。最もシンプルで一般的なジェネリクスの利用法は型パラメータです。
ジェネリック型の型パラメータにはかぎ括弧とアッパーキャメルケース(<Aaa, Bbb, ...>)が使われます。ジェネリックな型パラメータはたいていの場合<T>で示されます。Rustの場合、「ジェネリクス」には「1つ以上のジェネリックな型パラメータ<T>を受け付けるもの」という意味もあります。ジェネリックな型パラメータを指定された場合、それは必ずジェネリック型になり、そうでなければ必ず非ジェネリック型、すなわち具象型になります。
例として、あらゆる型の引数Tをとる ジェネリック関数 fooを定義すると
fn foo<T>(arg: T) { ... }
となります。Tはジェネリックな型パラメータに指定されているので、この場所で(arg: T)のように使用するとジェネリック型として扱われます。これはTという構造体がそれ以前に定義されていても同様です。
では、手を動かしながらジェネリック型の構文を体験していきましょう。
// `A`という具象型
struct A;
// `Single`という型を定義する際に`A`を使用していますが、その最初の使用よりも先に
// `<A>`がないため、また、`A`自身も具象型であるため、`Single`は具象型となります。
struct Single(A);
// ^ `Single`による`A`の一番最初の使用はここ。
// ここでは`<T>`が一番初めの`T`の使用よりも先に来ています。
// よって`SingleGen`はジェネリック型となります。
// なぜならば型パラメータ`T`がジェネリックだからです。
// `T`はどんな型にもなりえるため、上で定義した`A`を受け取ることもできます。
struct SingleGen<T>(T);
fn main() {
// `Single`は具象型で、`A`のみを受け取ります。
let _s = Single(A);
// `_char`という名の変数を生成します。これは`SingleGen<char>`
// という型で、値は`SingleGen('a')`となります。
// ここでは、`SingleGen`には明示的な型パラメータが与えられています。
let _char: SingleGen<char> = SingleGen('a');
// `SingleGen`型の変数には明示的に型パラメータを与えなくても構いません。
let _t = SingleGen(A); // 上で定義した`A`を使用。
let _i32 = SingleGen(6); // `i32`を使用。
let _char = SingleGen('a'); // `char`を使用。
}
参照
関数
「型Tはその前に<T>があるとジェネリック型になる」というルールは関数に対しても当てはまります。
ジェネリック関数を使用する際、型パラメータを明示する必要がある場合があります。返り値がジェネリック型である場合や、コンパイラが型パラメータを推論するのに十分な情報がない場合です。
型パラメータを明示したうえでの関数呼び出しの構文はfun::<A, B, ...>()のようになります。
struct A; // 具象型`A`
struct S(A); // 具象型`S`
struct SGen<T>(T); // ジェネリック型`SGen`
// 以下の関数は全て変数の所有権をとった後すぐにスコープを抜けて
// 変数をメモリ上から開放します。
// `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.
fn gen_spec_t(_s: SGen<A>) {}
// `gen_spec_i32`という関数を定義。
// これは明示的な型パラメータとして`i32`を与えられた
// `SGen<i32>`型の引数`_s`を取ります。
// この関数もジェネリックではありません。
fn gen_spec_i32(_s: SGen<i32>) {}
// `generic`という関数を定義。`SGen<T>`という型の引数`_s`を取ります。
// `<T>`が`SGen<T>`に先行しているため、これはTに対してジェネリックな関数です。
fn generic<T>(_s: SGen<T>) {}
fn main() {
// ジェネリックでない関数を使用します。
reg_fn(S(A)); // 具象型
gen_spec_t(SGen(A)); // 型パラメータ`A`を暗黙のうちに受け取ります。
gen_spec_i32(SGen(6)); // 型パラメータ`i32`を暗黙のうちに受け取ります。
// 型パラメータ`char`を明示的に`generic()`に渡します。
generic::<char>(SGen('a'));
// 型パラメータ`char`を暗黙的に`generic()`に渡します。
generic(SGen('c'));
}
参照
実装
関数と同様、implでメソッドを実装する際にもジェネリック型特有の記法が必要です。
#![allow(unused)]
fn main() {
struct S; // 具象型`S`
struct GenericVal<T>(T); // ジェネリック型`GenericVal`
// 型パラメータを指定したうえで、GenericValにメソッドを実装。
impl GenericVal<f32> {} // `f32`の場合のメソッド
impl GenericVal<S> {} // 上で定義した`S`への実装
// ジェネリック型のまま扱うには`<T>`が先に来る必要があります。
impl<T> GenericVal<T> {}
}
struct Val {
val: f64,
}
struct GenVal<T> {
gen_val: T,
}
// Valに対して実装。
impl Val {
fn value(&self) -> &f64 {
&self.val
}
}
// ジェネリック型`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());
}
参照
トレイト
もちろんトレイトもジェネリクスを活用することができます。ここではDropトレイトをジェネリックメソッドとして再実装し、自身と引数として受け取った値の両方をdropするようなメソッドにします。
// コピー不可な型
// 訳注: `clone()`メソッドを用いないかぎり、値のコピーではなくムーブが起きる型
struct Empty;
struct Null;
// ジェネリック型 `T`に対するトレイト
trait DoubleDrop<T> {
// `self`に加えてもう一つジェネリック型を受け取り、
// 何もしないメソッドのシグネチャを定義。
fn double_drop(self, _: T);
}
// `U`を`self`として、`T`をもう一つの引数として受け取る`DoubleDrop<T>`
// を実装する。`U`,`T`はいずれもジェネリック型。
impl<T, U> DoubleDrop<T> for U {
// このメソッドは2つの引数の所有権を取り、メモリ上から開放します。
fn double_drop(self, _: T) {}
}
fn main() {
let empty = Empty;
let null = Null;
// `empty`と`null`を開放。
empty.double_drop(null);
//empty;
//null;
// ^ TODO: これらの行をアンコメントしてみましょう。
}
参照
境界
ジェネリックプログラミングをしていると、型パラメータが特定の機能を持っていることを規定するため、トレイトに境界を設ける必要があることがよくあります。例えば、以下の例では、引数のDisplayトレイトを用いて出力を行うため、TがDisplayを持っていることを規定しています。つまり、「TはDisplayを実装 していなくてはならない 」という意味です。
// `Display`トレイトを実装している`T`を引数として取ります。
// `printer`という関数を定義。
fn printer<T: Display>(t: T) {
println!("{}", t);
}
境界は、ジェネリクスを全ての型ではなく、一定条件を満たす型に対してのみ適用するためにあります。つまり
struct S<T: Display>(T);
// エラー!`Vec<T>`は`Display`を実装していないため、この特殊化
// は失敗します。
let s = S(vec![1]);
境界のもう一つの効果は、ジェネリック型のインスタンスが、境界条件となるトレイトのメソッドにアクセスすることができるようになる点です。以下がその例です。
// 出力時のマーカー`{:?}`を実装するトレイト
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 }
// ジェネリック型`T`は`Debug`トレイトを実装していなくてはなりません。
// その限りにおいて、`T`がどのような具象型であろうとも次の関数は動作します。
fn print_debug<T: Debug>(t: &T) {
println!("{:?}", t);
}
// 「`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`.
}
付け加えておくと、where句を用いて境界を適用することもできます。場合によってはこちらの記法を使用したほうが読みやすくなる場合もあります。
参照
テストケース:空トレイト
トレイト境界の仕組みから、「トレイトがなにも機能を持っていなくとも境界条件として使用できることには変わりはない」という帰結がもたらされます。EqとCopyはstdライブラリにおけるそのような例です。
struct Cardinal;
struct BlueJay;
struct Turkey;
trait Red {}
trait Blue {}
impl Red for Cardinal {}
impl Blue for BlueJay {}
// 以下の関数はトレイト境界を設けていますが、そのトレイトが空である
// か否かとは関係ありません。
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`は`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: この行をアンコメントしてみましょう。
}
参照
std::cmp::Eq, std::marker::Copy, トレイト
複数の境界
+を用いて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 ^ ここをアンコメントしてみましょう。
compare_types(&array, &vec);
}
参照
Where句
トレイト境界は、{の直前にwhere句を導入することでも設けることができます。whereはさらに、型パラメータだけでなく任意の型に対して適用できます。
where句のほうが有効なケースには例えば以下のようなものがあります。
- ジェネリック型とジェネリック境界に別々に制限を加えたほうが明瞭になる場合
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}
// `where`を用いてジェネリック境界を設けます。
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB + TraitC,
D: TraitE + TraitF {}
where句の方が通常の構文より表現力が高い場合。この例では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:
impl<T> PrintInOption for T where
Option<T>: Debug {
// 出力されるのが`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();
}
参照
ニュータイプイディオム
ニュータイプイディオムは正しい型の値が与えられていることをコンパイル時に保証することができます。
For example, a function that measures distance in miles, must be given a value of type Miles.
struct Miles(f64);
struct Kilometers(f64);
impl Miles {
pub fn to_kilometers(&self) -> Kilometers {
Kilometers(self.0 * 1.609344)
}
}
impl Kilometers {
pub fn to_miles(&self) -> Miles {
Miles(self.0 / 1.609344)
}
}
fn is_a_marathon(distance: &Miles) -> bool {
distance.0 >= 26.2
}
fn main() {
let distance = Miles(30.0);
let distance_km = distance.to_kilometers();
println!("Is a marathon? {}", is_a_marathon(&distance));
println!("Is a marathon? {}", is_a_marathon(&distance_km.to_miles()));
// println!("Is a marathon? {}", is_a_marathon(&distance_km));
}
Uncomment the last print statement to observe that the type supplied must be Miles.
newtypeの元に使われている型のデータを取得するには、以下のようにタプルやデストラクト構文を用いることで取得できます。
struct Miles(f64);
fn main() {
let distance = Miles(42.0);
let distance_as_primitive_1: f64 = distance.0; // タプル
let Miles(distance_as_primitive_2) = distance; // デストラクト
}
参照
関連要素
関連要素とは複数の型の要素に関係のある規則の総称です。トレイトの拡張機能であり、トレイトの中で新しい要素を定義することを可能にします。
そのように定義する要素の一つに 関連型 があります。これにより、ジェネリックなコンテナ型に対するトレイトを使用する際に、よりシンプルな書き方ができるようになります。
参照
関連要素が必要になる状況
コンテナ型に、その要素に対してジェネリックなトレイトを実装した場合、そのトレイトを使用する者は全てのジェネリック型を明記 しなくてはなりません 。
以下の例ではContainsトレイトはジェネリック型AとBの使用を許しています。その後、Container型に対してContainsを実装していますが、その際後にfn difference()が使用できるように、A、Bはそれぞれi32と明記されています。
Containsはジェネリックトレイトなので、fn difference()では 全ての ジェネリック型を宣言しなくてはなりません。実際のところ、AとBは 引数 であるCによって決定されていて欲しいにも関わらず、です。これは次のページで紹介する関連型と呼ばれる機能によって可能です。
struct Container(i32, i32);
// 2つの要素がコンテナ型の中にあることをチェックするトレイト
// また、最初と最後の値を取得することもできます。
trait Contains<A, B> {
fn contains(&self, _: &A, _: &B) -> bool; // `A`と`B`両方を明示的に要求します。
fn first(&self) -> i32; // `A`、`B`いずれも要求しません。
fn last(&self) -> i32; // `A`、`B`いずれも要求しません。
}
impl Contains<i32, i32> for Container {
// コンテナ内の2つの要素が等しければTrueを返します。
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// ひとつ目の値を取得。
fn first(&self) -> i32 { self.0 }
// 最後(2つめ)の値を取得。
fn last(&self) -> i32 { self.1 }
}
// `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`と`B`は`type`キーワードを用いてトレイト内で宣言されています。
// (注意:この文脈で使用する`type`は型エイリアスを宣言する際の`type`とは
// 異なることに注意しましょう。)
trait Contains {
type A;
type B;
// これらの新しい型をジェネリックに使用するために、構文が
// アップデートされています。
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}
}
Containsトレイトを使用する関数において、AとBを明示する必要がなくなっていることに注目しましょう。
// 関連型を使用しない場合
fn difference<A, B, C>(container: &C) -> i32 where
C: Contains<A, B> { ... }
// 使用する場合
fn difference<C: Contains>(container: &C) -> i32 { ... }
前セクションの例を関連型を使用して書きなおしてみましょう。
struct Container(i32, i32);
// 2つの要素がコンテナ型の中にあることをチェックするトレイト
// また、最初と最後の値を取得することもできます。
trait Contains {
// メソッドが使用できるジェネリック型を定義。
type A;
type B;
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
impl Contains for Container {
// `A`と`B`がどの型であるかを明示。入力型(訳注:つまり`Self`の型)
// が`Container(i32, i32)`である場合、出力型は`i32`と`i32`となります。
type A = i32;
type B = i32;
// `&i32`の代わりに`&Self::A`または`&Self::B`と書いても構いません
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// ひとつ目の値を取得。
fn first(&self) -> i32 { self.0 }
// 最後(2つめ)の値を取得。
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));
}
幽霊型パラメータ
幽霊型とは実行時には存在しないけれども、コンパイル時に静的に型チェックされるような型のことです。
構造体などのデータ型は、ジェネリック型パラメータを一つ余分に持ち、それをマーカーとして使ったりコンパイル時の型検査に使ったりすることができます。このマーカーは実際の値を何も持たず、したがって実行時の挙動そのものにはいかなる影響ももたらしません。
以下の例では、そのようなマーカーとして幽霊型(std::marker::PhantomData)を用い、それぞれ異なった型の値を持つタプルを作成します。
use std::marker::PhantomData;
// ジェネリックなタプル構造体。2つ目のパラメータは幽霊型。
#[derive(PartialEq)] // 比較演算子(`==`)での比較を可能にします。
struct PhantomTuple<A, B>(A, PhantomData<B>);
// 同様に構造体を定義。
#[derive(PartialEq)] // 比較演算子(`==`)での比較を可能にします。
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }
// 注意点: ジェネリック型Aに対してはメモリが割り当てられていますが、
// Bには割り当てられていないため、計算に使うことはできません。
fn main() {
// ここで `f32` と `f64` は隠しパラメータです。
// <char, f32>と型宣言されたPhantomTupleを作成。
let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
// <char, f64>と型宣言されたPhantomTupleを作成。
let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);
// <char, f32>の型が与えられた構造体を作成。
let _struct1: PhantomStruct<char, f32> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
// 同様に<char, f64>の構造体
let _struct2: PhantomStruct<char, f64> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
// コンパイルエラー!型が違うので比較することができません!
// println!("_tuple1 == _tuple2 yields: {}",
// _tuple1 == _tuple2);
// コンパイルエラー! 型が違うので比較することができません!
// println!("_struct1 == _struct2 yields: {}",
// _struct1 == _struct2);
}
参照
テストケース:単位を扱う
共通の単位同士を扱う際のチェックのために、Addを幽霊型を用いた実装にすると便利な場合があります。その場合Addトレイトは以下のようになります。
// このように定義しておくと、`Self + RHS = Output`であることが保証され、
// かつ、impl時にRHSの型が明示されていない場合、デフォルトでSelfと同じに
// なります。
pub trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
// `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;
/// 単位を定義するため、空の列挙型を作成。
#[derive(Debug, Clone, Copy)]
enum Inch {}
#[derive(Debug, Clone, Copy)]
enum Mm {}
/// `Length`は`Unit`という幽霊型パラメータを持つ型ですが
/// 長さを表す型はジェネリック型ではなく`f64`です。
///
/// `f64`ははじめから`Clone`、`Copy`トレイトを持っています。
#[derive(Debug, Clone, Copy)]
struct Length<Unit>(f64, PhantomData<Unit>);
/// `Add`トレイトは加算演算子(`+`)の挙動を定義します。
impl<Unit> Add for Length<Unit> {
type Output = Length<Unit>;
// add()は`Length`の新しいインスタンスを返します。
// Lengthの中の値は合計値になっています。
fn add(self, rhs: Length<Unit>) -> Length<Unit> {
// ここでの`+`は`f64`の`Add`実装を呼び出します。
Length(self.0 + rhs.0, PhantomData)
}
}
fn main() {
// `one_foot`が幽霊型`Inch`を持つように明示します。
let one_foot: Length<Inch> = Length(12.0, PhantomData);
// `one_meter`が幽霊型`Mm`を持つように明示します。
let one_meter: Length<Mm> = Length(1000.0, PhantomData);
// 以下の`+`は上で定義した`Length<Unit>`用の`add()`メソッドを呼び出します。
//
// `Length`は`Copy`トレイトを持っているため、`add()`は`one_foot`と`one_meter`
// を消費する代わりにそのコピーを作り、`self`、`rhs`として扱います。
let two_feet = one_foot + one_foot;
let two_meters = one_meter + one_meter;
// 加算が問題なく実行されていることを確認。
println!("one foot + one_foot = {:?} in", two_feet.0);
println!("one meter + one_meter = {:?} mm", two_meters.0);
// 異なる単位間の加算は意味を成さないので、
// 以下はきちんとコンパイルエラーになります。
// コンパイルエラー:タイプミスマッチ
//let one_feter = one_foot + one_meter;
}
参照
借用 (&), 境界 (X: Y), 列挙型, impl & self, 演算子のオーバーロード, ref, トレイト (X for Y), タプル構造体.
スコープの規則
所有権、借用、ライフタイムといったRustに特有の概念において、変数のスコープは重要な役割を果たします。すなわち、スコープの存在によってコンパイラは借用は可能か否か、メモリ上の資源は解放可能か、変数はいつ作成され、いつ破棄されるか。といったことを知るのです。
RAII
Rustの変数は単にデータをスタック上に保持するだけのものではありません。例えばヒープメモリを確保するBox<T>のように、変数はメモリ上の資源を 保有 する場合もあるのです。RustはRAII(Resource Acquisition Is Initialization)を強制するので、オブジェクトがスコープを抜けると、必ずデストラクタが呼び出されてそのオブジェクトが保持していた資源が解放されます。
この振る舞いは リソースリーク バグを防ぐのに役立ちます。手動でメモリを解放したり、メモリリークバグにわずらわされたりすることはなくなるのです!簡単な例で説明しましょう。
// raii.rs
fn create_box() {
// 整数をヒープ上に確保。
let _box1 = Box::new(3i32);
// `_box1`はここで破棄され、メモリは解放されます。
}
fn main() {
// 整数をヒープ上に確保。
let _box2 = Box::new(5i32);
// ネストしたスコープ
{
// 整数をヒープ上に確保。
let _box3 = Box::new(4i32);
// `_box3`はここで破棄され、メモリは解放されます。
}
// お遊びで大量のボックスを作ります。
// もちろん手動で開放する必要はないよ!
for _ in 0u32..1_000 {
create_box();
}
// `_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))際は、資源の 所有権 が移動します。Rustっぽく言うと、「 ムーブ 」です。
資源を移動すると、それまでの所有者(訳注:変数などのこと)を使用することはできなくなります。これによりダングリングポインタの発生を防げます。
// この関数はヒープメモリ上の資源の所有権を取ります。
fn destroy_box(c: Box<i32>) {
println!("Destroying a box that contains {}", c);
// `c`は破棄されメモリは開放されます。
}
fn main() {
// _スタック_上に置かれた整数
let x = 5u32;
// `x`を`y`に *コピー* します。元の値が移動するわけではありません。
let y = x;
// 両方の値はそれぞれ独立に使うことができます。
println!("x is {}, and y is {}", x, y);
// `a`は_ヒープ_上の整数へのポインタ。
let a = Box::new(5i32);
println!("a contains: {}", a);
// `a`を`b`に *ムーブ* します。
let b = a;
// すなわち、`a`の指すメモリ上の番地が`b`にコピーされるため
// いずれもヒープ上の同じ値を指すポインタとなります。
// しかし所有権は`b`にあります。
// エラー!`a`は所有権を持たないため、ヒープ上のデータにアクセスできません。
//println!("a contains: {}", a);
// TODO ^ 試しにここをアンコメントしてみましょう。
// この関数はヒープメモリ上の所有権を`b`から取ります。
destroy_box(b);
// この時点でヒープメモリ上の資源は開放されているので、次の操作は
// 解放済みメモリをデリファレンスすることになります。
// しかしそれはコンパイラが許しません。
// エラー!上述の理由より
//println!("b contains: {}", b);
// TODO ^ 試しにここをアンコメントしてみましょう。
}
ミュータビリティ
データのミュータビリティは所有権を移譲した際に変更できます。
fn main() {
let immutable_box = Box::new(5u32);
println!("immutable_box contains {}", immutable_box);
// ミュータビリティエラー
//*immutable_box = 4;
// boxを *ムーブ* する、同時に所有権とミュータビリティを変更します。
let mut mutable_box = immutable_box;
println!("mutable_box contains {}", mutable_box);
// boxの内容を変更。
*mutable_box = 4;
println!("mutable_box now contains {}", mutable_box);
}
部分的ムーブ
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. Note that types that implement the Drop trait cannot be partially moved from, because its drop method would use it afterwards as a whole.
fn main() {
#[derive(Debug)]
struct Person {
name: String,
age: Box<u8>,
}
// Error! cannot move out of a type which implements the `Drop` trait
//impl Drop for Person {
// fn drop(&mut self) {
// println!("Dropping the person struct {:?}", self)
// }
//}
// TODO ^ Try uncommenting these lines
let person = Person {
name: String::from("Alice"),
age: Box::new(20),
};
// `name` は person からムーブしたが、 `age` は参照されています。
let Person { name, ref age } = person;
println!("The person's age is {}", age);
println!("The person's name is {}", name);
// エラー!部分的ムーブした値の借用:`person` では部分的ムーブが発生しています。
//println!("The person struct is {:?}", person);
// `person` は使用できませんが、
// `person.age` はムーブしていないので使用できます。
println!("The person's age from person struct is {}", person.age);
}
この例では、age変数をヒープ上に保持し、部分的ムーブを説明しています。上記コードでrefを削除すると、person.ageの所有権がage変数にムーブされるため、エラーになります。もしもPerson.ageがスタック上に保持されていたら、ageの定義がperson.ageをムーブすることなくデータをコピーするので、refは必須ではないのですが、実際にはヒープ上に保持されているためrefは必須です。
参照
借用
実際には、データの所有権を完全に受け渡すことなく一時的にアクセスしたいという場合がほとんどです。そのために、Rustでは 借用 という仕組みを用います。値そのもの(T)を受け渡すのではなく、その参照(&T)を渡すのです。
コンパイラは借用チェッカを用いて参照が 常に 有効なオブジェクトへの参照であることを、コンパイル時に保証します。つまり、あるオブジェクトへの参照が存在しているならば、そのオブジェクトを破壊することはできないということです。
// この関数はボックスの所有権を奪い、破棄します。
fn eat_box_i32(boxed_i32: Box<i32>) {
println!("Destroying box that contains {}", boxed_i32);
}
// この関数はi32を借用します。
fn borrow_i32(borrowed_i32: &i32) {
println!("This int is: {}", borrowed_i32);
}
fn main() {
// Create a boxed i32 in the heap, and an i32 on the stack
// Remember: numbers can have arbitrary underscores added for readability
// 5_i32 is the same as 5i32
let boxed_i32 = Box::new(5_i32);
let stacked_i32 = 6_i32;
// Boxの中身を借用。所有権を奪うわけではないため、
// 直後にもう一度借用できます。
borrow_i32(&boxed_i32);
borrow_i32(&stacked_i32);
{
// ボックス内の要素に対する参照を取得。
let _ref_to_i32: &i32 = &boxed_i32;
// エラー!
// ボックス内の要素が借用されているため、`boxed_i32`を破棄する
// ことはできません。
eat_box_i32(boxed_i32);
// FIXME ^ この行をコメントアウトしましょう
// Attempt to borrow `_ref_to_i32` after inner value is destroyed
borrow_i32(_ref_to_i32);
// ここで`_ref_to_i32`はスコープを抜け、借用もなくなります。
}
// `boxed_i32` can now give up ownership to `eat_box_i32` and be destroyed
eat_box_i32(boxed_i32);
}
ミュータビリティ
ミュータブルなデータは&mut Tでミュータブルに(変更可能な形で)借用することができます。これは ミュータブルな参照 と呼ばれ、読み込み・書き込みの権限を借用者に与えます。対照的に&Tはデータをイミュータブルな参照を介して借用し、借用した側はデータを読み込みはできますが書き込みはできません。
#[allow(dead_code)]
#[derive(Clone, Copy)]
struct Book {
// `&'static str`はread-onlyメモリ上の文字列への参照。
author: &'static str,
title: &'static str,
year: u32,
}
// この関数はBook型への参照を取ります。
fn borrow_book(book: &Book) {
println!("I immutably borrowed {} - {} edition", book.title, book.year);
}
// この関数はミュータブルなBook型へのミュータブルな参照を取り、
// `year`を2014へ変更します。
fn new_edition(book: &mut Book) {
book.year = 2014;
println!("I mutably borrowed {} - {} edition", book.title, book.year);
}
fn main() {
// `immutabook`という名のイミュータブルなBookを作成。
let immutabook = Book {
// 文字列リテラルは`&'static str`型になります。
author: "Douglas Hofstadter",
title: "Gödel, Escher, Bach",
year: 1979,
};
// `immutabook`のミュータブルなコピーを作成し、`mutabook`と名付けます。
let mut mutabook = immutabook;
// イミュータブルなオブジェクトをイミュータブルに借用します。
borrow_book(&immutabook);
// ミュータブルなオブジェクトをイミュータブルに借用します。
borrow_book(&mutabook);
// ミュータブルなオブジェクトをミュータブルに借用します。
new_edition(&mut mutabook);
// エラー!イミュータブルなオブジェクトをミュータブルに借用できません。
new_edition(&mut immutabook);
// FIXME ^ この行をコメントアウトしましょう
}
参照
エイリアス
データは一度にいくつでもイミュータブルに借用することができますが、その間オリジナルのデータをミュータブルに借用することはできません。一方でミュータブルな借用は一度に 一つ しか借用することができません。オリジナルのデータをもう一度借用できるのはミュータブルな参照が最後に使われた場所より あとで なければいけません。
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;
// データは元々の持ち主と参照の両方からアクセスすることができます。
println!("Point has coordinates: ({}, {}, {})",
borrowed_point.x, another_borrow.y, point.z);
// エラー!pointはすでにイミュータブルに借用されているため、
// ミュータブルに借用することができません。
// let mutable_borrow = &mut point;
// TODO ^ この行をアンコメントしてみましょう。
// 借用された値はここで再び利用されます。
println!("Point has coordinates: ({}, {}, {})",
borrowed_point.x, another_borrow.y, point.z);
// イミュータブルな参照がこれ以降のコードで利用されていないため、
// ミュータブルな参照として再借用できます。
let mutable_borrow = &mut point;
// ミュータブルな参照を介してデータを変更します。
mutable_borrow.x = 5;
mutable_borrow.y = 2;
mutable_borrow.z = 1;
// エラー!`point`はすでにミュータブルに借用されているため、
// イミュータブルに借用することはできません。
// let y = &point.y;
// TODO ^ この行をアンコメントしてみましょう。
// エラー!`println!`はイミュータブルな参照を取るため、printできません。
// println!("Point Z coordinate is {}", point.z);
// TODO ^ この行をアンコメントしてみましょう。
// OK!ミュータブルな参照は`println!`にイミュータブルな参照として渡せます。
println!("Point has coordinates: ({}, {}, {})",
mutable_borrow.x, mutable_borrow.y, mutable_borrow.z);
// ミュータブルな参照がこれ以降のコードで未使用なので、再借用できます。
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';
// 左辺に`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`は構造体をデストラクトする際にも有用。
let _copy_of_x = {
// `ref_to_x`は`point`の`x`フィールドへの参照。
let Point { x: ref ref_to_x, y: _ } = point;
// `point`の`x`フィールドへのコピーを返します。
*ref_to_x
};
// `point`へのミュータブルなコピー
let mut mutable_point = point;
{
// `ref`は`mut`とともに使い、ミュータブルな参照を取ることもできます。
let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point;
// `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);
// ポインタを含むミュータブルなタプル
let mut mutable_tuple = (Box::new(5u32), 3u32);
{
// `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.
fn main() {
let i = 3; // `i`のライフタイム開始 ───────────────────┐
// │
{ // │
let borrow1 = &i; // `borrow1`のライフタイム開始──┐│
// ││
println!("borrow1: {}", borrow1); // ││
} // `borrow1` 終了 ──────────────────────────────────┘│
// │
// │
{ // │
let borrow2 = &i; // `borrow2`のライフタイム開始──┐│
// ││
println!("borrow2: {}", borrow2); // ││
} // `borrow2` 終了 ──────────────────────────────────┘│
// │
} // ライフタイム終了 ───────────────────────────────────┘
ここで、一切の名前や型がライフタイムに代入されていないことに注意しましょう。これにより、ライフタイムの使われ方がこれから見ていくようなやり方に制限されます。
明示的アノテーション
借用チェッカーは参照がどれだけの間有効かを決定するために、明示的なアノテーションを使用します。ライフタイムが省略1されなかった場合、Rustは参照のライフタイムがどのようなものであるか、明示的なアノテーションを必要とします。
foo<'a>
// `foo`は`'a`というライフタイムパラメータを持ちます。
クロージャと同様、ライフタイムの使用はジェネリクスを必要とします。もう少し詳しく言うと、この書き方は「fooのライフタイムは'aのそれを超えることはない。」ということを示しており、型を明示した場合'aは&'a Tとなるということです。
ライフタイムが複数ある場合も、同じような構文になります。
foo<'a, 'b>
// `foo`は`'a`と`'b`というライフタイムパラメータを持ちます。
この場合は、fooのライフタイムは'a、'bの いずれよりも 長くなってはなりません。
以下はライフタイムを明示的に書く場合の例です。
// `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`を持つ関数
fn failed_borrow<'a>() {
let _x = 12;
// エラー:`_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.
}
fn main() {
// 下で借用するための変数を作成。
let (four, nine) = (4, 9);
// 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`.
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`.
}
参照
関数
省略をしない場合、ライフタイムのシグネチャ(e.g. <'a>)を持つ関数にはいくつかの制限があります。
- 全ての参照においてライフタイムを明示しなくてはなりません。
- 返り値となる参照はすべて引数と同じライフタイムか、
staticライフタイムを持たなくてはなりません
加えて、引数のない関数から参照を返すことは、それが結果的に無効なデータへの参照になるならば、禁止されています。ライフタイムを持つ関数の例をいくつか示します。
// 引数として`'a`のライフタイムで参照を一つ取ります。最低でもこの関数
// と同じだけの長さでなくてはなりません。
fn print_one<'a>(x: &'a i32) {
println!("`print_one`: x is {}", x);
}
// ミュータブルな参照でも同様。
fn add_one<'a>(x: &'a mut i32) {
*x += 1;
}
// 異なるライフタイムを持つ複数の引数がある場合。
// ここでは1種類のライフタイムでも問題ありませんが、より複雑なケースでは
// 異なるライフタイムが必要になる場合があります。
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
println!("`print_multi`: x is {}, y is {}", x, y);
}
// 受け取った参照をそのまま返すことに問題ありませんが、適切なライフタイム
// でなくてはなりません。
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x }
// `'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);
}
参照
メソッド
メソッドのライフタイムは関数に似ています。
struct Owner(i32);
impl Owner {
// 通常の関数と同様にライフタイムを明示。
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();
}
参照
構造体
構造体におけるライフタイムも関数のそれと似ています。
// `i32`への参照をメンバに持つ`Borrowed`型。
// 参照は`Borrowed`自体よりも長生きでなくてはなりません。
#[derive(Debug)]
struct Borrowed<'a>(&'a i32);
// 同様に、ここでも参照は構造体よりも長生きでなくてはなりません。
#[derive(Debug)]
struct NamedBorrowed<'a> {
x: &'a i32,
y: &'a i32,
}
// `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);
}
参照
トレイト
トレイトのメソッドにおけるライフタイムのアノテーションは、基本的には関数に似ています。implにもライフタイムのアノテーションがあることに注意してください。
// ライフタイムのアノテーションつき構造体
#[derive(Debug)]
struct Borrowed<'a> {
x: &'a i32,
}
// ライフタイムのアノテーションつきimpl
impl<'a> Default for Borrowed<'a> {
fn default() -> Self {
Self {
x: &10,
}
}
}
fn main() {
let b: Borrowed = Default::default();
println!("b is {:?}", b);
}
参照
境界
ジェネリック型