はじめに
はじめまして、task4233です。
これはあくまでメモなので、基本的にググってわかるようなことは詳しく書きません。
よろしくお願いします。
なお、以下のプログラムでは面倒なのでfn main() {}
を省略することがあります。
ご承知おきください。
おさらい
前回は4章の4.1-4.2に目を通して、以下のことを学びました。
- 変数宣言とその振る舞い
- 関数の書き方とその振る舞い
以下のリンクで参照できます。
https://qiita.com/task4233/items/0ecdf0e114daddbabd1d
4. シンタックスとセマンティクス
Rustをボトムアップで学びたいなら、この章を順番に読んでいくのが近道です。
だそうです。
4.3. プリミティブ型
「プリミティブ型」の種類とそれらの振る舞いを知り、扱えるようになること。
プリミティブ型
「プリミティブ型」とは標準ライブラリで提供される、最もプリミティブな型のことです。
ここで紹介するのは以下の種類のプリミティブ型です。
- ブーリアン型(
bool
) char
- 数値型
- 浮動小数点型(
f32
/f64
) - 整数
- 可変長型(マシンのポインタサイズと同サイズの整数型)
- 符号アリ(
isize
) - 符号ナシ(
usize
)
- 符号アリ(
- 固定長型
- 符号アリ(
i8
/i16
/i32
/i64
) - 符号ナシ(
u8
/u16
/u32
/u64
)
- 符号アリ(
- 可変長型(マシンのポインタサイズと同サイズの整数型)
- 浮動小数点型(
- 配列
str
- タプル
順番に見ていきます。
ブーリアン型(bool
)
ブーリアン型(bool
)は真偽値を表す型で、以下のようにtrue
とfalse
の2つの値のみを持ちます。
let x = true; // x: bool
let y: bool = false;
この型は4.5.で触れるif文で用いられます。
詳しくはこちらのドキュメントを参照してください。
char
char
は1つのUnicodeのスカラ値を表し、以下のように文字は'
で括ります。
let x = 'x'; // x: char
let thinking_face: char = '🤔';
なおcharが1バイトである他言語とは異なり、Rustのcharは4バイトです。
詳しくはこちらのドキュメントを参照してください。
エスケープシーケンスや、漢字等の振る舞いについて書かれています。
数値型
Rustには、数値型として大まかに「浮動小数点数」と「整数」に分けられます。
数値型 > 浮動小数点数
浮動小数点数とは、ざっくり言うと小数点以下を持つ数です。
この浮動小数点数を保持する数値型を「浮動小数点型」と言います。
Rustにはf32
およびf64
が存在しています。
以下のように、
f32
はサイズ32bitで浮動小数点数を表現し、
f64
はサイズ64bitで浮動小数点数を表現します。
fn main() {
let positive_num: f32 = 575.77;
let negative_num: f64 = -3.141592653589793238462;
}
浮動小数点型は、符号、指数部、仮数部に分けて扱われます。
仕組みに興味のある人は「浮動小数点型 仕組み」でググると良いかもしれません。
数値型 > 整数
整数とは、加法+と乗法xに関して環となる集合で……
と言うのは冗談で、小数点以下を持たない数です。
この整数を保持する数値型について、さらに「可変長」と「固定長」に分けられます。
数値型 > 整数 > 可変長
可変長とは、その名の通り長さが可変な数値型で「可変長型」と言います。
可変長は、使用しているマシンのポインタサイズと同サイズの整数型で、「可変長型」と言います。
例えば、32bitマシン上では4Byte(32bit)が、64bitマシン上では8Byte(64bit)が割り振られます。
この可変長型について、さらに「符号アリ」と「符号ナシ」に分けられます。
数値型 > 整数 > 可変長(マシンのポインタサイズと同サイズの整数型) > 符号アリ
符号アリとは、言い換えると正負の見分けをしていると言うことです。
この型をisize
と言います。
i
は整数(integer)の頭文字を取ったものです。
符号アリなので、以下のように正負どちらの値も変数束縛可です。
let positive_num: isize = 575;
let negative_num: isize = -77;
数値型 > 整数 > 可変長(マシンのポインタサイズと同サイズの整数型) > 符号ナシ
符号ナシとは、isize
とは異なり正のみの整数非負整数(0も可)
です。
この型はusize
と言います。
u
は符号ナシ(unsigned)の頭文字を取ったものです。
符号ナシなので、以下のように負の値は変数束縛不可です。
let positive_num: usize = 575;
let negative_num: usize = -77; // 負の値なのでコンパイルエラー
符号を区別するメリットは、以下の2点であると私は考えています。
- 誤って負の値を
u
の型に束縛してもエラーを吐くので「安全」であること - 負の値を持たないのが自明である場合、格納できる幅が2倍になること
数値型 > 整数 > 固定長
固定長とは、その名のとおり長さが固定されている数値で「固定長型」と言います。
Rustには8bit, 16bit, 32bit, 64bitの4種類の長さが存在します。
この4種類の固定長型について、可変長と同様に「符号アリ」と「符号ナシ」に分けられます。
数値型 > 整数 > 固定長 > 符号アリ
先ほどの可変長と同様にi
を付けることで符号アリの型になります。
以下のように、サイズを超える値を代入するとコンパイルエラーになります。
fn main() {
let i_08: i8 = 57577; // コンパイルエラー
let i_16: i16 = 57577; // コンパイルエラー
let i_32: i32 = 57577; // ok
let i_64: i64 = 57577; // ok
}
数値型 > 整数 > 固定長 > 符号ナシ
先ほどの可変長と同様にu
を付けることで符号ナシの型になります。
以下のように、サイズを超える値を代入するとコンパイルエラーになります。
ただし、符号アリの時にコンパイルエラーとなっていた16bitの変数は符号をなくしたため、範囲が広がりコンパイルが通るようになっています。
fn main() {
let u_08: u8 = 57577; // out of range for i8
let u_16: u16 = 57577; // ok
let u_32: u32 = 57577; // ok
let u_64: u64 = 57577; // ok
}
配列
配列の基本的な振る舞い
配列とは、固定長で同じ型の要素リストです。
配列はデフォルトでイミュータブルなので、ミュータブルな配列を作る際はmut
を付ける必要があります。
振る舞いは以下の通りです。
let a = [1, 2, 3]; // a: [i32; 3]
let mut m = [1, 2, 3]; // mut m: [i32; 3]
let s = [1, 2, '3']; // コンパイルエラー(i32とcharが混在している)
let f = [1, 2, 3.0]; // コンパイルエラー(i32とf32が混在している)
配列の一括初期化
配列の各要素を同じ値で初期化するためには、以下のように宣言します。
// let 配列名 = [初期化する値; 配列の要素数];
let a = [0; 20]; // a: [i32; 20]
let c = ['c', 575]; // c: [char; 575]
配列の要素数取得
配列の要素数は、以下のように.len()
で得られます。
let a = [0; 20]; // a: [i32, 20]
println!("a has {} elements.", a.len());
//[output] a has 20 elements.
配列内の特定要素に対するランダムアクセス
配列内の特定要素には、以下のように添字記法でアクセスできます。
ただし、添字は0-indexed
なので注意しましょう。
let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3]
println!("The second name is: {}", names[1]);
//[output] The second name is: Brian
スライス
「スライス」は他のデータ構造への参照(又は「ビュー」)です。
スライスを配列に適応すると、配列の要素に安全で効率的なアクセスをする際に便利になるそうです。
C++のstd::basic_string::substr()
のようなものでしょうか?
スライスは、&
と[]
を組み合わせて以下のように作れます。
let a = [0, 1, 2, 3, 4];
let complete = &a[..]; // aに含まれる全ての要素を持つスライス
let middle = &a[1..4]; // 1、2、3のみを要素に持つaのスライス
スライスは型&[T]
を持ちます。
この型については、4.18で詳しく触れます。
str
Rustの
str
型は最もプリミティブな文字列型です。サイズ不定型のように、それ単体ではあまり便利ではありませんが、&str
のように参照の後ろに置かれたときに便利になります。
これについては、4.9, 4.17, 4.32にて掘り下げます。
タプル
タプルの基本的な振る舞い
タプルは固定サイズの順序ありリストで、以下のように書けます。
C++のstd::pair
のようなものでしょうか。
let x = (1, "hello"); // 型推論あり
let xx: (i32, &str) = (1, "hello"); // 型推論なし
タプルの束縛
タプルもデフォルトでイミュータブルなので、ミュータブルなタプルを作るためにはmut
を付けます。
let mut x = (1, 2); // x: (i32, i32)
let y = (2, 3); // y: (i32, i32)
x = y;
y = x; // コンパイルエラー(yはイミュータブルなので)
タプルのフィールドに対するアクセス
タプルのフィールドには分配束縛let
を通じて、以下のようにアクセスできます。
ただし、要素数1のタプルに対してはlet (x,)
のように,
を付ける必要があります。
そうしないと分配束縛と認識されないので注意しましょう。
C++のstd::basic_ios::tie
のようなものでしょうか。
let (x, y, z) = (1, 2, 3);
println!("x is {}.", x);
// [output] x is 1.
let (a,) = (57,); // カンマを入れないとコンパイルエラー
println!("a is {}.", a);
// [outptut] a is 57.
また、タプルのフィールドには、以下のようにインデックス構文でもアクセスすることができます。
インデックス構文は配列と同様に0-indexed
ですが、[]
ではなく.
を使用します。
let tuple = (1, 2, 3);
let x = tuple.0;
let y = tuple.1;
let z = tuple.2;
println!("x is {}.", x);
// [output] x is 1.
関数
4.2.で扱った以下のような関数ポインタは、型を持ちます。
fn add_one(x: i32) -> i32 {
x + 1
}
fn main() {
let x: fn(i32) -> i32 = add_one;
println!("{}", x(1));
}
// [output] 2
ここで、xは「i32型の引数を受け取ってから、1を加算してi32型を戻す関数」への関数ポインタです。
Rustには、ブーリアン型、char、複数の数値型、タプル等のプリミティブ型が存在し、基本的にはイミュータブルである。
扱いについては、型に気を付けないとコンパイルエラーを引き起こすため、型に注意する。
4.4. コメント
コメントの種類とどのような時に使えるのかを知ること。
コメント
今まで何度か使用してきましたが、コメントはコンパイル時に読まれずに無視される部分です。
Rustには「行コメント」と「ドキュメンテーションコメント」の2種類が存在します。
順番に見ていきましょう。
行コメント
行コメントは//
以降の全ての文字をコメントとみなすコメントのことです。
また、/*
と*/
で囲んだ部分もコメントと見なされます。
ただし、見づらくなるためオススメではない……かも?
以下のように、プログラムの補足、説明に使います。
let x = 5; // x: i32
/* もし長めの説明を書くのであれば、行コメントを複数行に渡って書くこと
ができる。 */
// //とコメントとの間にスペースを置くことでより読みやすくなる。
ドキュメンテーションコメント
先ほどの行コメント以外のコメントはドキュメンテーションコメントです。
ドキュメンテーションコメントは、以下のように//
の代わりに///
を用い、Markdown記法が使用できます。
このMarkdown記法は、後述するHTMLドキュメントを生成するrustdocツールの時に役立ちます。
/// 与えられた数値に1を与える
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, add_one(5));
/// # fn add_one(x: i32) -> i32 {
/// # x + 1
/// # }
/// ```
fn add_one(x: i32) -> i32 {
x + 1
}
ここで、新しい関数assert_eq!()
という関数が出てきていますが、この関数は2つの引数が等しくない場合はpanic!します。
test用の関数ということですかね。
また、///
の他のドキュメンテーションコメントの他に//!
があります。
//!
は、その後に続く要素ではなく、それを含んでいる要素(例えばクレート、モジュール、関数)にコメントを付けます。 一般的にはクレートルート(lib.rs)やモジュールルート(mod.rs)の中で使われます。
//!
は以下のように利用します。
//! # Rust標準ライブラリ
//!
//! Rust標準ライブラリはポータブルなRustソフトウェアをビルドするために不可欠な
//! ランタイム関数を提供する。
rustdoc
rustdocは、ドキュメンテーションコメントからHTMLドキュメントを生成するツールです。
このrustdocはコード例をテストとして実行するためにも使用できます。
以下のようなコードをrustdocにかけて、HTMLドキュメントを作成してみましょう。
/// 与えられた数値に1を与える
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, add_one(5));![スクリーンショット 2018-09-05 12.31.19.png](https://qiita-image-store.s3.amazonaws.com/0/238364/e22f925d-6991-111f-68ab-0956e2dcd381.png)
/// # fn add_one(x: i32) -> i32 {
/// # x + 1
/// # }
/// ```
fn add_one(x: i32) -> i32 {
x + 1
}
ドキュメントはソースコードおよび単体のMarkdownファイルから作成できるそうですが、今回はソースコードから作成します。
コマンドとしては以下の2通りあります。
-
cargo doc
コマンドを利用する方法 -
rustdoc
コマンドを利用する方法
前者は簡単にドキュメントを作成でき、後者はカスタマイズがしやすくいと感じました。
それぞれ説明しますので、好きな方を使うと良いと思います。
cargo doc
コマンドを利用する方法
cargo doc
コマンドを利用する場合、Cargo.toml
のある階層で以下のコマンドを叩くことで作成できます。
$ cargo doc --open
このrust_tutorial
というのは私のCargoプロジェクト名です。
どうやら、そこにはCargoプロジェクト名が表示されるようです。
右上の[src]をクリックすると、以下のようにソースコードが表示されるはずです。
ドキュメンテーションコメントが表示されていませんね……?
調べてみると、関数はデフォルトでプライベートなので、関数の前にpub
を付けてpublicにする必要があるようです。
これは4.25で詳しく触れます。
ソースコードを以下のように修正します。
/// 与えられた数値に1を与える
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, add_one(5));
/// # fn add_one(x: i32) -> i32 {
/// # x + 1
/// # }
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
改めて$ cargo doc --open
を叩きます。
今度は正しくドキュメンテーションコメントが表示されました!
なお、このHTMLドキュメントは
./target/doc/[Cargoプロジェクト名]/
にindex.html
という名前で作成されているはずです。
rustdoc
コマンドを利用する方法
rustdoc
コマンドを利用する場合、以下のようなコマンドを叩くことでHTMLドキュメントを作成できます。
コマンドはどこでも叩けますが、srcディレクトリのある階層で叩くことをオススメします。
$ rustdoc [対象の.rsファイルのパス]
叩くと、叩いた階層にdocディレクトリができているはずです。
docディレクトリの階層は以下のようになっているはずです。
$ cd doc && tree ./
./
├── COPYRIGHT.txt
├── FiraSans-LICENSE.txt
├── FiraSans-Medium.woff
├── FiraSans-Regular.woff
├── Heuristica-Italic.woff
├── Heuristica-LICENSE.txt
├── LICENSE-APACHE.txt
├── LICENSE-MIT.txt
├── SourceCodePro-LICENSE.txt
├── SourceCodePro-Regular.woff
├── SourceCodePro-Semibold.woff
├── SourceSerifPro-Bold.woff
├── SourceSerifPro-LICENSE.txt
├── SourceSerifPro-Regular.woff
├── jquery.js
├── main
│ ├── add_one.v.html
│ ├── fn.add_one.html
│ ├── index.html
│ └── sidebar-items.js
├── main.css
├── main.js
├── normalize.css
├── rustdoc.css
├── search-index.js
└── src
└── main
└── main.rs.html
今回作成したHTMLドキュメントは、./doc/main/index.html
です。
開くと、以下のようなページが表示されるはずです。
このmain
というのは、私の.rsファイル名です。
この表示されているクレート名をカスタマイズしたい場合は、以下のようにrustdoc
コマンドに--name-crate
オプションを付ければ良いです。
$ rustdoc [.rsファイルのパス] --crate-name [付けたいクレート名]
私はdocs
という名前を付けたかったので、
$ rustdoc ./src/main.rs --crate-name docs
を叩いて、
./doc/docs/index.html
を確認すると、以下の以下のようにクレート名がdocs
になりました!
コメントには「行コメント」と「ドキュメンテーションコメント」がある。
前者は通常時に使用し、//でコメントアウトする。
後者はドキュメント作成等に使用し、///でコメントアウトする。
ドキュメント作成は、「rustdoc」もしくは「cargo doc」で行える。
おわりに
4章4.3-4.4では大まかに以下の2つが書かれていました。
- プリミティブ型の種類と基本的な使用法
- コメントの種類とその活用法
これで、各種プリミティブ型とコメントを使用することができるようになりました(ほんまか?)
次はif、ループを見ようと思います。