###### tags: `Rust` `const` `static` `type` `as (cast)` # Rust テキスト学習 8 # const と inline ## const 定数を定義する。 constはライフタイムすべてで使える ただし、メモリ上にリソースがアロケートされることはなく インラインで値が書き換わるので効率的。 ## static staticはグローバル変数とも呼ばれる。 インライン化は行われない。 メモリ上に固定の位置を持ち、ただ1つのインスタンスのみが存在する。 プログラム全体でライフタイムが続く 'static ライフタイムという ## static mut mutableなstatic変数を用意する。 static mutな変数へのアクセスは unsafe な操作であり、 unsafe ブロックで行う必要がある。 ```rust= const c: i32 = 5; static stv: i32 = 10; static mut stmutv = 100; unsafe { stmutv += 10; println!("{}", stmutv); } ``` 大抵の場合は、static と const のどちらかを選べるときは constを使ったほうが最適化できる。 クレート全体で同じ値を使い回すときも、constのほうが良い。 # type エイリアス type キーワード もともとある型に別名をつける ```rust= type Alias = String; let a: Alias = "hello".to_string(); ``` # 型のキャスト 2種類ある - as : 安全なキャスト - transmute : 危険なキャスト(任意) ## 型強制 type coercion = 型強制 あるデータから他のデータ型への自動的な、または暗黙的な変換 型強制と型変換はよく似ている - 型強制 : 暗黙的 - 型変換 : 暗黙的 or 明示的 ## Deref カスタマイズ型強制を定義するトレイト ## as 明示的で安全なキャスト いくつかルールが有る - i/u は符号付き、なしで挙動が変わる - f > i/u へのキャストは、0方向への丸めを行う - f64 > f32 へのキャストは最も近い値が作られる(精度が落ちる) ```rust= let x: i32 = 5; let y = x as i64; // ポインタキャスト let a = 300 as *const char; let b = a as u32; ``` ## transmute as キャストだと以下のコードはエラー ```rust= let a = [0u8,0u8,0u8,0u8]; let b = a as u32; ``` 4byteの0で埋められた配列を u32に変換する。 という行為は、危険なキャストである。 配列という型を異なる型であるu32として扱おうとしているため。 std::mem にある transmute モジュールを使うことで、変換できる。 当然、unsafe ブロックで包まないと成功しない。 使う人間任せの自己責任なコード。 ```rust= use std::mem; unsafe { let a = [0u8, 0u8, 0u8, 0u8]; let b = mem::transmute::<[u8; 4], u32>(a); } ``` # 関連型 Rustの型システムの強力な部分 複数の型をグループ化する ## Graph の例 Node と Edge の2つのstructを使う Graphトレイト ジェネリクス\<N, E> を使って下記のようにトレイトを書く ```rust= trait Graph<N, E> { fn has_edge(&self, &N, &N) -> bool; fn edges(&self, &N) -> Vec<E>; // etc } ``` このようなトレイトを定義すると、このトレイトを実装する方を引数とするメソッドを書くとき、 ジェネリクスパラメータの記述が長くなる。 ```rust= fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... } ``` ## 関連型の定義 trait ブロックの中に、使うジェネリック型を type 演算子で定義。 そして、メソッド内で type で定義したジェネリック型を使うだけ。 関連型を使うとこのようにかけ。 ```rust= trait Graph { type N: fmt::Display; // トレイト境界もつけられる type E; fn has_edge(&self, &Self::N, &Self::N) -> bool; fn edges(&self, &Self::N) -> Vec<Self::E>; // etc } ``` ## 関連型を使用しているトレイトの実装 implブロックでは、メソッドの実装に加えて、 type キーワード での型エイリアスの定義を用いて、 ジェネリックパラメータを実際の型に置き換える。 ```rust= struct Node; struct Edge; struct MyGraph; impl Graph for MyGraph { type N = Node; type E = Edge; fn has_edge(&self, n1: &Node, n2: &Node) -> bool { true } fn edges(&self, n: &Node) -> Vec<Edge> { Vec::new() } } ``` # 演算子オーバーロード std::ops; クレートにあるトレイトを使って、 構造体に +-/*% などの演算子を使ったときの振る舞いを実装できる ## Addトレイト ```rust= pub trait Add<RHS = Self> { type Output; fn add(self, rhs: RHS) -> Self::Output; } ``` let z = x + y; という式があったとき、 - x = Self - y = RHS(ジェネリックパラメータ) - z = Self::Output - + = 左右を引数に取る add() メソッド の型になる。 Outputは関連型なので、 impl ブロックで型を定義する。 ## Derefトレイト 参照外し演算子 * をオーバーロードする。 *(ポインタ変数) で 中身が見れるようになるので、参照外しという。 型強制などに有効