Rust半端ないってもぉー!アイツ半端ないって!
めっちゃ馴染みない概念あるもん…
そんなんできひんやん普通、そんなんできる?

前置き

この記事はまだRustをよく知らない人が勉強しながら書いているので、もしかしたら間違ったことが書いてあるかもしれません。というか多分間違いがあります。ご容赦ください。

参考文献

これから入門しようという方は、こんな便所の落書きも同然の記事を読むより、公式のリファレンスや先人の方々の記事を読んだほうが早いです。
The Rust Programming Language 日本語版
Rust入門

今回の記事に限らず、このシリーズの記事では基本的に上記のサイトを参考にする予定です。

前提知識的なもの

今までC/C++やPythonを触ってきた人視点で書いていきます。

式と文の区別

  • 式:何かしらの値を返す。
  • 文:値は返さない。

オブジェクト

数値や関数などはすべてオブジェクトと呼ばれる。
つまり、数値「1」「0.1」やmain関数などもオブジェクトに分類される。

この辺はPythonに近い?

変数と束縛

変数はオブジェクトの一つ。型によらず、let文で宣言する。
オブジェクトと変数は独立した概念で、let文によってオブジェクトと結びつける。これを束縛というらしい。

例:変数aとオブジェクト「10」を束縛

fn main() {
  let a = 10;
}

上記のプログラムはC言語をはじめ多くの言語では「代入」と呼ばれる操作ですが、Rustでは代入という操作一つに対しても複数の概念があるようですね。

可変性

変数は標準で不変になるらしい。
つまり、C言語でいうと、普通に変数を宣言したら勝手に定数(const)になるということか。
でも、不変変数とは別に定数という概念もあるらしい。なんのこっちゃ。

こんな、他の言語ならエラーなく実行できそうなプログラムも、Rustではエラーになる。

fn main() {
  let a = 1;
  a = 2;			// NG
}

変数aは不変変数だからだ。
ちなみにRustは同じ変数に対して何度でも束縛できるので、letを使えば不変変数の値を変えることができる。

fn main() {
  let a = 1;
  let a = 2;	// OK
}

ほかに、以下のようにして宣言時から最初から可変変数にすることもできる。

fn main() {
  let mut a = 1;
  a = 2;			// OK
}

使い分けはイマイチわからないけど、公式リファレンスによればバグ予防やインスタンスのコピーにかかる時間、計算量といった観点から考えるべき、とのこと。
このあたりは実際になにか作ってみてコツを掴んだほうがいいのかな?

変数と定数は異なる

つまり、C言語でいうと、普通に変数を宣言したら勝手に定数(const)になるということか。
でも、不変変数とは別に定数という概念もあるらしい。なんのこっちゃ。

とか書いたけど、やはり定数と不変変数に違いはあるようだ。
定数では、データ型を必ず指定しなければならないとのこと。

fn main() {
  const A: i32 = 100;
}

※i32は整数のデータ型
定数にも定数を使うメリットがあるらしい。でも現時点では読んでもよくわからなかった。そのうち分かればヨシ!

データ型

整数型

ビット長 符号付き C言語でいうと… 符号なし C言語でいうと…
8bit i8 char u8 unsigned char
16bit i16 short u16 unsigned short
32bit i32 long (int) u32 unsinged long (unsigned int)
64bit i64 long long u64 unsigned long long
arch isize 該当なし? usize 該当なし?

isize, usizeというのは、動作するコンピュータに依存するらしい。つまり、64bitOS(CPU)なら64ビットに、32bitOS(CPU)なら32ビットになるということか。

浮動小数点数

種類 符号付き
単精度浮動小数点数(32bit) f32
倍精度浮動小数点数(64bit) f64

こちらはシンプル。

論理値型

bool型。C++にあるboolと同じ。(C言語でもstdbool.hをincludeすれば使えるらしいけど)

fn main() {
  let a = true;					// 不変変数で真
  let b: bool = false;	// 定数で偽
}

文字型

char型。文字を扱える。’で文字をくくる。

fn main() {
  let a = 'A';
  println!("{}", a);
}

出力:

A

C++のstring型と同じように、文字列を扱うこともできる。文字列については後日触れる予定。

複合型

複数の型の値を一つの複合型にまとめ上げる。タプル型と配列がある。

タプル型

複数の型の一つにまとめる。
C++でいうpairみたいなものか。(追記:失礼。C++(std)にもタプルはある模様)

fn main() {
  let tup: (i32, f64, bool) = (100, 33.4, false);
  println!("{}, {}, {}", tup.0, tup.1, tup.2);
}
100, 33.4, false

値を参照するには、.0、.1、…と、ピリオド+数字で参照できる。
上記の例では型を指定しているが、指定しなくてもいいらしい。

fn main() {
  let tup = (100, 33.4, false);
  println!("{}, {}, {}", tup.0, tup.1, tup.2);
}

また、このようにして値を取り出すこともできる。

fn main() {
  let tup = (100, 33.4, false);
  let (x, y, z) = tup;
  println!("x={}", x);
}
x=100

さらに、必要ない値はアンダーラインを使えば無視できる。

fn main() {
  let tup = (100, 33.4, false);
  let (x, _, _) = tup;					// tup.1, tup.2は無視
  println!("x={}", x);
}
x=100

配列型

C言語にもある概念ですね。
タプルとは異なり、すべて同じ型でなければならないようです。

fn main() {
  let a = [1, 2, 3, 4, 5];
  println!("{}", a[0]);
}
1

これはC言語の配列と同じで固定長なので、サイズを伸縮させることは出来ないようです。
可変長配列はベクタ型といい、これは後日触れたいと思います。

終わりに

疲れたので今日はここまで。
最初から知らない概念ばかりで頭がパンクしそうになりましたが、少しずつ慣れていきたいと思います。