前回の記事
https://qiita.com/on_c98/items/a829d61973483921b9cb

Rustのデータ型

Rustは静的型付け言語です。すべての値は型を持ちます。
Rustには下記のようなデータ型があります。

スカラー型

整数型

    • i8, i32, i64, i128, isize

 

    u8, u32, u64, u128, usize

iはinteger、uはunsigned integerを意味します。数字はそれぞれビット数を表しています。

isizeとusizeはそれぞれ実行環境に適したサイズになります。64ビットのプロセッサで動いている場合は64ビットに、32ビットのプロセッサで動いている場合は32ビットになります。

let negative_num: i32 = -1300;
let positive_num: u32 = 4000;

整数型は下記のような書き方をすることで16進数、8進数、2進数、バイト値を表現できます。読みやすくするためにアンダーバーを使い桁区切りをすることも出来ます。値には影響しません。(1,000のコンマのようなもの)

    • アンダーバー: 1_000_000 (1000000と同値)

 

    • 16進数: 0xff 先頭にゼロと小文字のx(エックス)をつける。

 

    • 8進数: 0o77 前頭にゼロと小文字のo(オー)をつける。

 

    • 2進数: 0b0000_1111 先頭にゼロと小文字のb(ビー)をつける。

 

    バイト値: b’a’ 小文字のb(ビー)とシングルクォーテーションをつける。シングルクォーテーションで囲った値は後に出てくるchar型になりますが、先頭にbをつけることでバイト値として扱われます。

どの型を使用したらいいか分からない時は、i32を使用することが推奨されています。

浮動小数点数型

Rustの浮動小数点数型には下記の二種類の方があります。signedのみで、unsignedはありません。

    f32, f64
let fnum: f32 = 3.14;

演算子

Rustでは下記の演算子を利用できます。

    • 加算: 1 + 2

減算: 10 – 3

乗算: 4 * 3

除算: 10 / 2

剰余: 20 % 4

違う型同士では計算できません。

let a = 3.14 * 10 // エラー

let b: i8 = 10;
let c: i32 = 100;
let d = b + c; // エラー

整数同士で計算を行った場合、結果は全てi32になるため、除算で余りが出た場合は切り捨てられます。

20 / 3(答えは6になる)

下記のように記述するとf64として扱われます。

20.0 / 3.0(答えは6.666666666666667になる)

u8やf32など他のサイズで受け取りたい場合は、格納する変数の型を指定するか、as句を用います。

    • let x: u8 = 100 + 155;

 

    let f = 20.0 as f32 / 3.0

論理型

Rustの論理型は下記になります。

    bool

論理型はtrueとfalseの2値のみを持ちます。条件式に使用できます。

let is_true: bool = true;

if is_true {
    println!("yes, it is true.");
}

比較演算子を用いた式の結果は、論理型になります。

let correct: bool = 10 > 2; // true
let incorrect: bool = 3 == 5; // false

文字型

Rustの文字型には下記があります。

    char
let word: char = 'a';

一文字のみを表現します。文字をシングルクォーテーションで囲うことで文字型として扱われます。

後に出てくる&strやStringとは別物になります。扱えるのは一文字のみで、複数文字は表現できません。

let w = ‘hello’; (エラー)

文字列リテラル型

文字列リテラル型には、下記があります。

    &str

こちらはchar型と違い複数文字を扱うことができます。

let sentence: &str = “hello world”;

&は参照を表します。Rust内で出てくる文字列リテラルはすべて参照になります。

文字列リテラルは常に固定値であり、値を変化させることができません。

複合型 compound types

タプル tuple

    (1,2,3)

タプルは固定長のリストのようなものです。複数の型を保持できます。

タプルの値は分割代入という方法を用いて、変数に割り当てることができます。

let tuple: (i32,f64,char) = (-400, 5.421, 't');

// 分割代入
let (a, b, c) = tuple

println!("{a}"); // -400
println!("{b}"); // 5.421
println!("{c}"); // t

タプルの各要素にはtuple.[index]のようにドットとインデックスを用いてアクセスできます。
インデックス外の要素にアクセスしようとした場合、コンパイルエラーになります。

let tuple = ("test", 100, -10);

println!("{}", tuple.0); // test
println!("{}", tuple.1); // 100
println!("{}", tuple.2); // -10
println!("{}", tuple.3); // コンパイルエラー

配列 array

    [1,2,3]

Rustのリストにはタプルの他に配列があります。タプルと違い全て同じ型になります。

他の言語と違い、Rustの配列は固定長になります。宣言時にサイズが決まっている必要があります。可変長の配列を使用したい場合、主に後に出てくるVec型を用います。

[型; サイズ]のように記述します。

let array: [i32; 5] = [1,2,3,4,5];

下記のように記述することで、0という値を5つ持った配列を作成することができます。

let zeros = [0; 5]; // [0, 0, 0, 0, 0]

配列の各要素には、array[index]のように記述することでアクセスできます。
インデックス外の要素にアクセスしようとした場合、panic!例外が発生してプログラムが終了します。

そのような場合は.get()メソッドを用いると、戻り値がOption型になるので同時に例外処理を実装することができます。

let first = zeros[0];
let second = zeros[1];
let out_of_index = zeros[5]; // panic!
let without_panic = zeros.get(10).unwrap_or(&999);
println!("{}",without_panic) // 999

独自型

structやenumを用いることで独自の型を宣言することができます。Rustにはclassは実装されておらず、structがその代わりのような役目を果たします。実装方法は、別の章で解説します。今回は、Rustの標準ライブラリに実装されている方について簡単に説明していきます。

Rustの標準ライブラリには下記のような型が存在します。

構造体 struct

Rustの標準ライブラリに含まれる構造体は、スカラー型の拡張型のようなものが多いです。StringやVecは特に頻繁に使用され、関連するメソッドも多いです。

String: 可変長の文字列型。&strと相互に変換できる。

Vec: ベクタ型。可変長の配列。固定長の配列がスタックだったの対し、こちらはヒープでメモリ上に値を格納する。

Box: ボックス型。Rustではデフォルトで値はスタックされるが、それをヒープ化する。Boxのように用いる。

HashMap<K,V>: ハッシュマップ型。他の言語の辞書型のようなもの。キーとバリューの組み合わせで値を格納する。

let hello: String = String::from("Hello");
let vector: Vec<i32> = Vec::new();
vector.push(10);
vector.push(50);

列挙子 enum

Rustの標準ライブラリには下記二つの列挙子が実装されています。二つは主に例外処理に用いられ、メソッドの戻り値をこれらの型でラッパーして返すことで、値を安全に処理することができます。

Result::{Ok, Err}: リザルト型。OkとErrの2つの状態を持つ。

Option::{Some, None}: オプション型。RustではNULLを実装しない代わりに、こちらのオプション型を用いて表現する。

let ok: Result<i32, i32> = Reuslt::Ok(100);
let op: Option<i32> = Option::Some(0);

終わりに

次回は各項目を掘り下げる前に、Rustの関数について書いていこうと思います。