実践 Rust 入門 第4章プリミティブ型
Rust における型
- すべての値の型はコンパイル時に決定される
- これは安全性、効率性という利点がある
- 安全性: エラーの早期発見、所有権システムとの連携による型安全かつデータ競合のないプログラム開発の促進
- 効率性: メモリの細かい制御、コンパイラによる最適化の恩恵
- これは安全性、効率性という利点がある
型の分類
定義による分類
- プリミティブ型
- ユーザ定義型
構成による分類
- スカラ型
- 複合型
スカラ型
- ユニット
- 真理値
- 固定精度の整数
- 固定精度の浮動小数点
- 文字
- 参照
- 生ポインタ
- 関数ポイント
let n = 40; let c = 'R';
ユニット
- 値を返さない関数の戻り値はユニット型
- ユニット型は意味のある情報を持たないため、サイズは0
- キーバリューのペアを格納するデータ構造でキーのみデータを保持したい場合には、バリューにユニットを格納すると無駄にメモリを消費しない
assert_eq!(std::mem::size_of::<()>(), 0);
真偽値
- true と false を持つ
let b1 = true; let b2 = !b1; let n1 = 1; let n2 = 2; let b3 = n1 >= n2; let b4 = n1 < n2; let b5 = b3 && b4; let b6 = b3 || b4;
固定精度の整数
- ビット幅指定の整数型
- 符号なし (uxx)
- 符号あり (ixx)
let n1: i32 = 32;
- アドレス幅の整数型
- usize
- isize
let n2: usize = 32;
- 整数リテラル
- デフォルト10進数
- プレフィクスで他の基数に基づく数を表される
- 0x: 16進数
- 0o: 8進数
- 0b: 2進数
let h1 = 0xff; let o1 = 0o744; let b1 = 0b1010_0110_1110_1001;
- サフィックスで型を指定可能
let n1 = 10u8;
- ASCII 文字に対する文字コードを得ることも可能
let n2 = b'A'; assert_eq!(n2, 65u8);
代表的な整数演算
整数型のメソッドや定数
- 整数型のメソッド
- pow(), abs(), rotate_left(), from_str(), to_string(), checked_add()
- 定数
- 最大値 MAX 等
整数演算の桁あふれ
- リリースモードでは桁あふれは検出できない
- 以下の方法で対応するメソッドを使用することを検討する
- 検査付き演算
- 飽和演算
- ラッピング演算
- 桁あふれ演算
- 以下の方法で対応するメソッドを使用することを検討する
fn overflow_calc() { let n1 = 200u8; let n2 = 3u8; assert_eq!(n1.checked_mul(n2), None); assert_eq!(n1.saturating_mul(n2), std::u8::MAX); assert_eq!(n1.wrapping_mul(n2), 88); assert_eq!(n1.overflowing_mul(n2), (88, true)); }
固定精度の浮動小数点
- ビット数に応じてf32, f64の2つの型が用意されている
- サフィックスで型を指定可能
- 指数部も指定可能
let f1 = 10.0; let f2 = 1_234.897f64; let f3 = 576.6E77;
文字
- 1つのUnicodeのスカラ値
- シングルクォートで囲む
- 1文字を表すのにたとえ英数字であっても4バイトを消費する
- 複数のコードポイントが組み合わされた文字例えば'が'などは char リテラルとして使用することはできない(コンパイルエラー)
let c1 = 'A'; let c2 = 'B'; assert!(c1 <= c2); assert!(c1.is_uppercase()); let c3 = '0'; assert!(c3.is_digit(10)); let c4 = '\t'; let c5 = '\''; let c6 = '\\'; let c7 = '\x7F'; let c8 = '漢'; let c9 = '\u{5b57}'; let c10 = '\u{1f600}'; println!("{}", c10);
参照
- メモリ安全ポインタ
- データが格納されている場所を指す
- usize と同じビット幅の整数で表される
fn f1(mut n: u32){ n = 1; println!("{}", n); } fn f2(n_ptr: &mut u32){ println!("f2: n_ptr {:p}", n_ptr); *n_ptr = 2; println!("{}", n_ptr); } let mut f_n1 = 0; f1(f_n1); println!("{}", f_n1); f2(&mut f_n1); println!("{}", f_n1);
生ポインタ
- メモリ安全ではないポインタ
- dereference するときに unsafe ブロック内に処理を記述する必要性がある
let mut c1 = 'A'; let c1_ptr: *mut char = &mut c1; assert_eq!(unsafe{*c1_ptr}, 'A'); unsafe { *c1_ptr = 'B'; }
関数ポインタ
- 関数を指すポインタ
fn double(n: i32) -> i32 { n + n } fn abs(n: i32) -> i32 { if n >= 0 { n } else { -n } } let f1: fn(i32) -> i32 = double; let f2: fn(i32) -> i32 = abs; assert_eq!(std::mem::size_of_val(&f1), std::mem::size_of::<usize>()); let mut f3 = double; assert_eq!(std::mem::size_of_val(&f3), 0);