Open2

Rust 基礎メモ

yukiyuki

一般的なプログラミングの概念

変数と可変性

  • 変数定義は let で標準で不変となる(再代入不可)
fn main() {
	let x = 5;
	println!("{}", x); // 5 と出力される
	x = 6;
	println!("{}", x); // コンパイルエラーとなる
};
  • 可変にする場合は let の後に mut キーワードをつけることで可変となる(再代入可能)
    • mut は mutable の略
fn main() {
	let mut x = 5;
	println!("{}", x); // 5 と出力される
	x = 6;
	println!("{}", x); // 再代入されて 6 と出力される
};
  • println! は “print line” の意味で、指定されたデータを標準出力に出力して、その後に改行をするマクロ
    • ! 記号はマクロであることを示している

変数と定数(constants)の違い

  • 変数の let はコンパイルの段階で値が決まらなくても値を代入することができるが、再代入ができない
    • 型宣言は無くても ok
  • 定数は const で宣言が可能で、コンパイル時定数として扱われるため、コンパイル時点でどのような値なのかが決まっている必要がある
    • 型宣言は必須で、命名規則は大文字で単語はアンダースコア区切り

シャドーイング

  • let で同じ名前の変数を再宣言すると、新しい変数は前の変数を覆い隠し(シャドーイングし)、変数使用時には再宣言した変数の値が使われる
  • ただしブロックによってスコープが決まっており、スコープ内で再宣言した変数はスコープを出るともともと宣言していたスコープ外の変数の値に戻る
fn main() {
    let x = 5;
    let x = x + 1; // この時点で x は 6

    {
        let x = x * 2; // ブロック内で再定義することでシャドーイングされる
        println!("{}", x); // 12 と出力される
    }

    println!("{}", x); // ブロックから出たのでブロック外で定義していた x が使用され 6 が出力される
}
  • 違う型として再宣言する場合は、再代入ではなく新しい変数宣言の扱いとなるためエラーにはならない
fn main() {
    let spaces = "   "; // 文字列型で宣言
    let spaces = spaces.len(); // 数値型の宣言だが同じ名前の変数となるが、シャドーイングによって最初に宣言した変数は覆い隠される

    let mut spaces = "   "; 
    spaces = spaces.len(); // 違う型の再代入の場合はコンパイルエラーとなる
}

データ型

  • Rust は静的型付き言語であるため、コンパイル時にすべての型が判明している必要がある
  • let で型宣言を省略した場合は値によって型推論してくれる

スカラー型

  • 単独の値を表す型で、 4 つのスカラー型が存在する
    • 整数
    • 浮動小数点数
    • 論理値
    • 文字

整数

  • 整数は小部分のない数値で、何ビットの整数なのかと、符号付き・無しによって型が存在する

    大きさ 符号付き 符号なし
    8-bit i8 u8
    16-bit i16 u16
    32-bit i32 u32
    64-bit i64 u64
    arch isize usize
    • arch は動作しているコンピュータによって変わる
    • 整数型の基準は i32 型で、 64 ビットシステム上でも、この方が最速となる

浮動小数点

  • f32f64 の 2 つの型があり、基準型は f64 となる
    • 現代の CPU では f32 とほぼ同スピードにもかかわらす、より精度が高いため

数値演算

  • 足し算などの数値演算は同じ型同士で行う必要がある
fn main() {
	let x: usize = 6;
	let y: f64 = 1.5;
	// let z = x / y; この場合は演算する変数の型が異なるためエラーとなる
  let z = (x as f64) / y; // 整数を小数点に変換して演算する
	println!("{}", z) // 4 が出力される
}

論理値

  • 他の言語と同様 true, false で表し、bool 型と指定する

文字型

  • 文字にはユニコードの 1 文字を表す char 型がある
    • 32 bit 長
    • 文字をシングルクォーテーションで囲む
    • 文字型とは別に文字列型として &strString 型があり、文字はダブルクォーテーションで囲む

文字列型(記事に記載はないが気になって調べた)

  • &str
    • そのまま文字列を代入できる
      • let str: &str = ”this is str";
    • immutable
    • 文字列( utf-8 のバイト列)への参照
      • 文字列はリテラルになり、文字分のメモリが確保される
      • メモリは文字が入る大きさしか割り当てられないので immutable となる
      • 文字列は参照になるため、変数も参照型にしなくてはならない
      • 参照型にするためには変数の型の前に & が必要なので、str の前に「&」がついている
    • プリミティブ型
  • String 型(※ Rust の基本データ型ではなく Rust の標準ライブラリで提供されているもの)
    • 文字列の初期化?かなんやらが必要そう
      • let str: String = String::from(”this is String”);
    • mutable
    • utf-8 文字列であることが保証される
    • ヒープメモリ上に配置される
      • Rust にはデータを保持するために 3 種類のメモリ空間があり、そのうちの 1 つがヒープメモリでプログラム実行時に作られるデータとなる
      • ヒープメモリはプログラムの実行につれて動的に確保され、領域は順次拡大する
        • そのため「積み重なる」「堆積する」などという意味からヒープと呼ばれる

複合型

  • 複数の値を 1 つの方にまとめたもので、Rust にはタプルと配列の 2 種類がある

タプル型

  • 複数の型を 1 つにまとめた型で、タプルの位置ごとに型があり、それぞれ同じ型である必要はない
fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}
  • タプルから個々の値を取得するには分解して変数に代入する
fn main() {
    let tup = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("The value of y is: {}", y);
}
  • ピリオドに続けてインデックスを指定しても取得できる
fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);
    let five_hundred = x.0;
    let six_point_four = x.1;
    let one = x.2;
}

配列

  • 複数の値を 1 つにまとめた型で、全要素同じ型でなければならない
  • また、Rust の配列は固定長のため、コンパイル時に長さが固定される
    • ベクタ型という長さを変える型もあるよう
  • 宣言は角括弧の中に要素の型とセミコロン、その後に要素数を宣言する
let a: [i32; 5] = [1, 2, 3, 4, 5];
  • 初期値と配列の長さを与えることで同じ値の初期値がつくれる
let a = [3; 5]; // let a = [3, 3, 3, 3, 3]; と同じ
  • 他の言語と同じ様にインデックスでアクセスできる
fn main() {
    let a = [1, 2, 3, 4, 5];
    let first = a[0];
    let second = a[1];
}
  • コンパイル時はエラーが出なかった場合でも、実行時に存在しないインデックスでアクセスするとパニックする
    • パニックはプログラムがエラーで終了したことを表す Rust 用語