3. 一般的なプログラミングの概念
- 前回の記事
https://qiita.com/revioness/items/44b28b2b4db3303a518e - 今回は前回使用している、説明しているところもあるのでそこらへんはざっくり行きます
3.1 変数と可変性
-
前回の記事では、変数は標準でイミュータブル(不変)になると書いた
それが以下// イミュータブル let val1: i32 = 5; // ミュータブル let mut val2: i32 = 5; val1 = 6 //コンパイルエラー val2 = 6 //OK
再代入する場合は必ずletの後にmutをつける必要がある
-
変数と定数の違い
Rustはjsなどと同じよう、constで定数を定義することができる
この定数は必ず型宣言が必要とのことconst MAX_POINTS: u32 = 100_000;
- constとイミュータブルの違いは?
どちらも不変であり、違いがないように見える。実際正確には把握していないが、調べてみると- letは型宣言が不要だがconstは必要
- constはどんなスコープ(グローバルスコープ)でも定義可能というところ
- constとイミュータブルの違いは?
-
シャドーイング
前に定義した変数と同じ名前の変数を新しく宣言でき、 新しい変数は、前の変数を覆い隠します。Rustaceanはこれを最初の変数は、 2番目の変数に覆い隠されたと言い、この変数を使用した際に、2番目の変数の値が現れるということです。
fn main() { let x = 5; let x = x + 1; { let x = x * 2; println!("The value of x in the inner scope is: {}", x); } println!("The value of x is: {}", x); }
これはイミュータブルでも問題ない
理由としては、let x = 5
でイミュータブルで宣言した後に、再度宣言しなおしているため、元のxという変数にx + 1を入れるのではなく、新しいxという変数に過去のxに1をプラスした値を入れているという感じlet mut x = 5; x = "This is String";
このようにするとコンパイルエラーになる
mutでミュータブルにしても元の型と違う値は入れることができない
3.2 データ型
- Rustは静的型付け言語であり、すべての変数の型はコンパイル時に必ず判明している必要がある
- 複数の型が予想される場合は型注釈をつけなければいけない
let guess: u32 = "42".parse().expect("Not a number!");
スカラー型
Rustには主に4つのスカラー型があります: 整数、浮動小数点数、論理値、最後に文字です。
スカラーは簡単にいうと普通の数字。大きさのみを持っている
相対するものとしてベクトルというものがある
ベクトルは大きさに加え、方向があるもの
-
整数型
大きさ 符号付き 符号なし 8-bit i8 u8 16-bit i16 u16 32-bit i32 u32 64-bit i64 u64 arch isize usize Rustでは8bitから最大64bitまでの整数を表現することが可能
bitとは二進数で表されるものであり、8bitで1バイトとなる
例として、8bitで表せる最大値は11111111
で255
となる
これを符号付きの場合は先頭の1bitが符号を表すため、1111111
で±127
までの値を表せる
isize
とusize
はプログラムが動作しているアーキテクチャに依存するとのこと
(32bitなら32bit、64bitなら64bit) -
浮動小数点型
Rustにはさらに、浮動小数点数に対しても、2種類の基本型があり、浮動小数点数とは数値に小数点がついたもののことです。 Rustの浮動小数点型は、f32とf64で、それぞれ32ビットと64ビットサイズです。基準型はf64です。 なぜなら、現代のCPUでは、f32とほぼ同スピードにもかかわらず、より精度が高くなるからです。
fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32 }
型を定義せずに小数を格納した場合は自動的に
f64
型になるらしい
(速度が同じなら基本的に小数を使う際はf64型でいいかも) -
数値演算
これはほかの言語と変わらないので飛ばす -
論理値型
他の多くの言語同様、Rustの論理値型も取りうる値は二つしかありません: trueとfalseです。 Rustの論理値型は、boolと指定されます。
fn main() { let t = true; let f: bool = false; // with explicit type annotation // 明示的型注釈付きで }
型宣言をしてもしなくても問題ないらしい
たびたび型宣言をしなくても自動的に解釈してくれるとのこと -
文字型
Rustには文字も用意されています。Rustのchar型は、 言語の最も基本的なアルファベット型
fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻'; //ハート目の猫 }
char型の場合はシングルクォテーションで囲うこと
char型はユニコードのスカラー値を表している
https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF
これだけの文字をchar型だけで表現することが可能 -
複合型
複合型というと少しわかりづらい感じはあるが、配列やタプルがそれにあたる-
タプル型
宣言は以下fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); }
letのあとに変数名で、そのあとに型をひとつづつ書いていく(少しめんどくさそう)
しかしこれらはほかの変数と同じく型宣言がなくてもOKfn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("The value of y is: {}", y); }
- アクセス方法
let tup = (500, 6.4, 1); //上にあるように、最初に宣言して、その下でx, y, zという変数にそれぞれ独立させて入れる let (x, y, z) = tup; println!("The value of y is: {}", y); //配列のようにインデックス番号でアクセス println!("The value of y is: {}", tup.1);
- アクセス方法
-
配列型
ほかの言語と同じように宣言可能fn main() { let a = [1, 2, 3, 4, 5]; }
タプル型と違って、この配列の中のデータの型はすべて同じでなければいけない
また、Rustの配列は一度宣言されたらサイズを伸ばすことも縮めることもできないので注意
似たような型としてベクタ型というものがあるが、これは8章で解説されるらしい
-
3.3 関数
-
続いては関数の書き方について
-
Rustはmain関数が実行時のエントリポイント(最初に走る処理)になっている
fn main() { println!("Hello, world!"); another_function(); } fn another_function() { println!("Another function."); // 別の関数 }
型の宣言は
fn
の後に関数名、引数といった感じで定義する
関数名はスネークケースで定義するのが慣習(単語の間を_でつなぐ)- 似たようなものとしてキャメルケースがある(単語の変わり目を大文字で書く 例:anotherFunction)
覚えておいて損はない
引数がある場合は以下
fn another_function(x: i32) { println!("The value of x is: {}", x); // xの値は{}です }
関数名のあとの()の中に
変数名: 型
といった書き方をする
関数の引数は必ず型の宣言が必要とのこと-
戻り値のある関数
戻り値に関しては少し特殊な部分がある
戻り値がある関数は以下のように定義fn another_function() -> i32{ 5 } //returnでも返せる fn another_function() -> i32{ return 5; }
アロー
->
のあとに戻り値の型を宣言
これを見たときに、少しでもプログラミングをかじっている人はおかしいと思うかもしれないがRust的にはこれでOK
Rustは戻り値がある関数を作成した時にreturn文を書かないと、最後に評価した値が戻り値となる特性を持っている(この場合は;は必要ない)
(正直returnで明確に指定したほうが可読性があがるのでは?と思うがどうだろうか)
- 似たようなものとしてキャメルケースがある(単語の変わり目を大文字で書く 例:anotherFunction)
3.4 コメント
// これでコメントアウトになる
この程度のことしか書かれてないので飛ばす
ドキュメントコメントに関してはここでは解説されていない
3.5 制御フロー
- これも少しわかりづらくかかれているが、ようはループやif文のこと
if文
fn main() {
let number = 3;
if number < 5 {
println!("condition was true"); // 条件は真でした
} else {
println!("condition was false"); // 条件は偽でした
}
}
if文はこんな感じ
条件式にかっこが必要ないところはpythonに近い
-
三項演算子
Rustもほかの言語同様三項演算子がある
書き方は以下fn main() { let condition = true; let number = if condition { 5 } else { 6 }; // numberの値は、{}です println!("The value of number is: {}", number); }
これはifの後の条件式を評価して、trueだったらelseの手前の値、falseだったらelse以降の値がnumberに入ることになる
この場合はnumberには5が入るfn main() { let condition = true; let number = if condition { 5 } else { "six" }; println!("The value of number is: {}", number); }
この場合はエラーになる
評価した後に変数に入れる値の型は同一でなければならない
先ほども言ったように、変数の型は単独でなければならないというルールに従っている
ループ処理
- Rustのループ処理は、
loop while for
の三つが存在する-
loop
fn main() { loop { println!("again!"); // また } }
loopはこのように書くことで無限にループさせることができる
このloopを抜けるにはbreak
を使う必要があるfn main() { let mut loops: i32 = 0; const LIMIT: i32 = 10; loop { if loops == LIMIT{ break; } println!("again!"); // また loops+=1; } }
これで10回ループさせることができる
-
while
fn main() { let mut number = 3; while number != 0 { println!("{}!", number); number -= 1; } // 発射! println!("LIFTOFF!!!"); }
これはよくあるwhileと同じなので飛ばす
単純にwhileのあとの式が真の間は繰り返すだけ -
for
fn main() { let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {}", element); } }
for文の基本はpythonと同じ仕組み
この文だとelementの中には10, 20とaの値が順番に入っていく
ループで一番多く使うのはこのfor文fn main() { for number in (1..4).rev() { println!("{}!", number); } println!("LIFTOFF!!!"); }
ここが少し特殊な書き方になる
inの後を(1..4)のようにすることで範囲を表すことができる
この場合は1以上4未満の間となる
例えば1..=4
とした場合は1以上4以下にすることも可能
rev
メソッドは逆順に並べ替えてくれるメソッドらしい
ちなみにこのnumberはミュータブルであり、ループの中で値を書き換えることが可能fn main() { for number in (1..4).rev() { println!("{}!", number+1); } println!("LIFTOFF!!!"); }
4! 3! 2! LIFTOFF!!!
-