感想

一章では Rust とは何か、といった部分を学びました。
この章では、Rust の基本的な文法を学びます。

本書

https://www.amazon.co.jp/gp/product/4798061700/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1

コアライブラリ

数値型

符号あり整数型

i で始まる

名前 ビット数 値の範囲
i8 8 -128 ~ 127
i16 16 -32768 ~ 32767
i32 32 -2147483648 ~ 2147483647
i64 64 -9223372036854775808 ~ 9223372036854775807
i128 128 -170141183460469231731687303715884105728 ~ 170141183460469231731687303715884105727
isize 32 or 64 32bit 環境では i32、64bit 環境では i64

符号なし整数型

u で始まる

名前 ビット数 値の範囲
u8 8 0 ~ 255
u16 16 0 ~ 65535
u32 32 0 ~ 4294967295
u64 64 0 ~ 18446744073709551615
u128 128 0 ~ 340282366920938463463374607431768211455
usize 32 or 64 32bit 環境では u32、64bit 環境では u64

浮動小数点型

f で始まる

名前 ビット数 値の範囲
f32 32 1.175494351e-38 ~ 3.402823466e+38
f64 64 2.2250738585072014e-308 ~ 1.7976931348623158e+308

isize と usize について

isize と usize という型は、アーキテクチャのメモリ間に依存してサイズが変わってくる。

  • 32bit 環境では i32、u32 になり、64bit 環境では i64、u64 になる。
  • usize は主に、配列やベクタの要素にアクセスしたり、サイズを表現したりする際に使われる。

文字列型

コアライブラリのなかで定義されている文字列型は str のみ。
しかし、Rust で書かれたコードを見ると String という型もよく目にする。

str

文字列スライスと呼ばれる型。
スライスとは、メモリ上に存在する文字列データのスタート地点と長さを示すもの。

例)
アプリケーションの中であらかじめ定義された文字列があり、それを参照して&strの形で利用する

例のように、肩の名前に&をつけることで、その型の参照を表すようになる。

String

標準ライブラリで定義されている文字列型。
文字列データの変更や、長さの変更が可能。文字列操作を行う際には、String 型を利用する。

str と String の違い

str と String は、どちらも UTF-8 でエンコードされた文字列データを格納している。そしてお互いに型変換をすることができる。

String を str に変換するには、ポインタと文字列長をコピーしてスライスを作る。
そのため文字列自体のコピーは発生せず、メモリは圧迫しない。だが、&str を String に変換する時はメモリの確保が行われるので、長い文字列は注意が必要。

let s1: String = String::from("Hello World");
let s2: &str = &s1; // String --> &str
let s3: String = s2.to_string(); // &str --> String

タプル

異なる型を収めることができる集合のこと。関数から複数の値を返すときに、まとめてタプルで返すことができる。
タプルは内部に格納された肩の全てを含めて一つの肩を構成する。そのためタプル内のコードの一部の型を後から変更することはできない。

内部の値にアクセスするには、ドット記法を使う。

let mut t = (1, "2");
t.0 = 2;
t.1 = "3";

配列

特定の型の値を連続に収めた集合のこと。配列のサイズは固定で、コンパイル時に決まっている必要がある。
内部の値にアクセスするときは、[]を使う。

また、配列を参照するときには、自動的にスライスとして扱われる。
スライスとして扱うと、[start, end]のような範囲指定ができるようになるので便利

let mut a: [i32; 3] = [0, 1, 2];
let b: &[i32; 3] = &a[0; 3];
a[1] = b[1]
a[2] = b[2]
println!("{:?}", &a[1..3]);

ユーザー定義型

Rust では、構造体と列挙型の 2 種類のユーザー定義型を定義することができる。

構造体(stract)

struct Person {
    name: String,
    age: u32,

}

let p = Person {
    name: String::from("John"),
    age: 8,
}

列挙型(enum)

Rust の列挙型の特徴として、それぞれの列挙子にさらにデータを付与することができる。
付与するデータの型や構造は、全く違うものでも構わない。

enum Event {
    Quit,
    KeyDown(u8),
    MouseDown { x: i32, y: i32 },
}

let e1 = Event::Quit;
let e2 = Event::MouseDown { x: 10, y: 10 };

標準ライブラリ

Option

Option は、データが存在する場合と存在しない場合を表現できる列挙型。

pub enum Option<T> {
    None,
    Some(T),
}

データが存在しない場合は None、存在する場合は、その型を T としてとき Some(T)となる。

Result

Result は、処理の結果が成功か、エラーかを表現できる列挙型。

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

処理が成功した場合には、任意の型 T のデータを持つ Ok(T)を使用する。エラーの場合には、任意の型 E のデータを持つ Err(E)を使用する。

let result: Result<i32, String> = Ok(200);

match result {
    Ok(code) => println!("code: {}", code),
    Err(err) => println!("Err: {}", err),
}

このコード例は、「result が Ok(~)という構造をしているときに、~に該当する部分をローカル変数 code に束縛して次のブロック内で使用する」という意味

let result: Result<i32, String> = Ok(200);

if let Ok(code) = result {
    println!("code: {}", code);
}

Result 型を match や if let で処理すると、ソースコードのネストが深くなったり、重厚な印象を与えるため、それを回避するために、unwrap_or()を使用する。
これは Ok()だった場合はそのまま展開し、Err()だった場合は引数で与えた値を返す。
他にも、unwrap_or_else()というものもある

また、and_then()は Ok()だった場合にだけ、指定した引数を実行することができる。

Vec

Vec はベクタ型。配列とは違い、内部に収められる要素の数を増減させることができ、初期化を便利に実装するための vec![]マクロが用意されている。
vec![]を使用することで、あらかじめ要素を詰めた状態を作ることができる。

let v1 = vec![1,2,3,4,5] // 1 ~ 5の数を入れて初期化
let v2 = vec![0; 5] // 0を5つ入れて初期化

Box

Box は、ヒープ領域にデータを確保するためのポインタ型。Box は、データをヒープ領域に確保するためのポインタ型で、データの所有権を持つ。

Box を使うとできるようになること

  • コンパイル時にサイズがわからない型を格納すること
  • 大きなサイズの型の値を渡す際に、データの中身をコピーせず、ポインタで渡すこと
  • 共通のトレイトを実装した様々な型を画一的にポインタで扱うこと
fn main(){
    let byte_array = [b'h', b'e', b'l', b'l', b'o'];
    print(Box::new(byte_array));
    let byte_array = [b'w', b'o', b'r', b'l', b'd', b'!'];
    print(Box::new(byte_array));
}

fn print(s: Box<[u8]>) {
    println!("{:?}", s);
}

Box はコンパイル時にサイズ不定の値でも、実行時にそのサイズがわかれば、ヒープ領域に確保することができる。

変数宣言

let と mut

値を変数に束縛するには let を使用する。(束縛とは変数名に値を紐づけること)
Rust では、変数デフォルトで変更不可。なのでもし変数を変更不可にするのなら、変数宣言に、mut をつける

const と static

const と static はいずれも定数を定義できるキーワード。

const は常に変更不可、別の値を紐づけようとしたり、変更することはできない。
static は、定数を定義するときに使用するキーワード。定数の値は、コンパイル時に決定される。