[[📚The Rust Programming Language|the book]]の第19章。 # Unsafe Rust - ハードウェアや[[OS]]などの実装で必要になることがある - [[生ポインタ]] - [[生ポインタと参照やスマートポインタとの違い]] - `unsafe`キーワードがなくても[[生ポインタ]]の生成はできる - ただし[[参照外し]]はできない ```rust fn main() { let num = 5; let r1 = &num as *const i32; // Dereference of raw pointer requires unsafe function or block println!("{}", *r1); } ``` [[unsafeブロック]]の中では動作する。 ```rust fn main() { let num = 5; let r1 = &num as *const i32; unsafe { println!("{}", *r1); // 5 } } ``` - [[Unsafe Rust]]で[[生ポインタ]]を使う具体例 - Cコードとのインターフェースとして - [[借用チェッカー]]には理解できない安全な抽象を構成するとき - [[unsafe関数]] - [[unsafeブロック]]の中でのみ呼べる [[unsafeブロック]]の外だとエラーになる。 ```rust unsafe fn dangerous() {} fn main() { // Call to unsafe function requires unsafe function or block [E0133] dangerous() } ``` `split_at_num`は[[Unsafe Rust]]を使っている。 ```rust #[stable(feature = "rust1", since = "1.0.0")] #[inline] #[track_caller] #[must_use] pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { assert!(mid <= self.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. unsafe { self.split_at_mut_unchecked(mid) } } ``` safeに実装しようとすると以下のようになる。 ```rust fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { let len = slice.len(); assert!(mid <= len); // Cannot borrow `*slice` as mutable more than once at a time. (&mut slice[..mid], &mut slice[mid..]) } ``` - [[Rust]]の[[借用チェッカー]]は上記を危険とみなす - 同じ[[スライス (Rust)|スライス]]から2回[[借用]]している - [[特定のスコープである特定データに対して1つしか可変参照を持てない]] という規則を破っている - しかし上記は参照の範囲が明確に異なるため、恐れているデータ競合や不整合は存在しないはず - [[unsafeブロック]]で囲むだけでは問題は解決しない ```rust fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { let len = slice.len(); assert!(mid <= len); // これはunsafeは必要ないと言われてエラーになるだけ unsafe { (&mut slice[..mid], &mut slice[mid..]) } } ``` [[unsafeブロック]]だけなく、[[生ポインタ]]や[[unsafe関数]]を利用する必要がある。 ```rust use std::slice; fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { let len = slice.len(); // スライスの生ポインタ let ptr = slice.as_mut_ptr(); assert!(mid <= len); unsafe { ( // ptrからmidの数だけsliceを取得 slice::from_raw_parts_mut(ptr, mid), // 先頭はmidだけずれる & 取得する要素は全体からmidの分だけ減る slice::from_raw_parts_mut(ptr.add(mid), len - mid), ) } } ``` [[extern]]キーワードを使って[[FFI]]を実現する。 - [[externブロック]] これだけで[[C言語]]の関数使えるの凄い。。。 ```rust extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("Cのabs: {}", abs(-3)) } } ``` > [!info] > `extern`は[[Rust]]以外の言語から[[Rust]]の関数を呼び出す場合にも使用される。 - 2つのスレッドが、同じ可変のグローバル変数にアクセスしているとデータ競合を起こす - [[static変数]] - [[Rustにおけるstatic変数と定数の違い]] - [[Rustで可変なstatic変数を読み書きするときはUnsafe Rustが必要]] - 実際は[[Unsafe Rust]]ではなく[[スマートポインタ]]を使おう - unsafeな[[トレイト]] - 少なくとも1つの[[メソッド (Rust)|メソッド]]に[[コンパイラ]]が確かめられない不変条件? - [[トレイト]]の型に、[[Syncトレイト]]と[[Sendトレイト]]を実装していない型が存在する場合は`unsafe impl`を使う必要あり # 高度な[[トレイト]] https://doc.rust-jp.rs/book-ja/ch19-03-advanced-traits.html [[Iteratorトレイト]]のコードを見る。 ```rust pub trait Iterator { /// The type of the elements being iterated over. #[stable(feature = "rust1", since = "1.0.0")] type Item; #[lang = "next"] #[stable(feature = "rust1", since = "1.0.0")] fn next(&mut self) -> Option<Self::Item>; ``` `Iterator`トレイトを実装するものは[[関連型]]`Item`の指定が必要。 制限付きカウンターを試しに実装してみる。 ```rust struct Counter { count: u32, max: u32, } impl Counter { fn new(max: u32) -> Self { Counter { count: 0, max } } } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> { self.count += 1; if self.count < self.max { Some(self.count) } else { None } } } fn main() { let counter = Counter::new(10); for cnt in counter { eprintln!("cnt = {:?}", cnt); } } ``` [[ジェネリクス (Rust)|ジェネリクス]]ではなく[[関連型]]を使う理由は? たとえば ```rust trait Say { type Extra; fn hello(&self) -> (String, Self::Extra); fn bye(&self) -> (String, Self::Extra); } struct Human { name: String, } impl Human { fn new(name: String) -> Self { Human { name } } } impl Say for Human { type Extra = i32; fn hello(&self) -> (String, Self::Extra) { (format!("Hello, {}", self.name), 1) } fn bye(&self) -> (String, Self::Extra) { (format!("Bye, {}", self.name), 2) } } fn main() { let ichiro = Human::new("Ichiro".to_string()); eprintln!("ichiro.hello() = {:?}", ichiro.hello()); eprintln!("ichiro.bye() = {:?}", ichiro.bye()); } ``` これは[[関連型]]に指定された型をオマケとして返すような実装。上の例では[[関連型]]に`i32`を指定している。これは[[ジェネリクス (Rust)|ジェネリクス]]と何が違うのか? [[関連型]]の実装ポイント ```rust trait Say { type Extra; fn hello(&self) -> (String, Self::Extra); fn bye(&self) -> (String, Self::Extra); } impl Say for Human { type Extra = i32; fn hello(&self) -> (String, Self::Extra) { (format!("Hello, {}", self.name), 1) } fn bye(&self) -> (String, Self::Extra) { (format!("Bye, {}", self.name), 2) } } ``` これを[[ジェネリクス (Rust)|ジェネリクス]]にしてみる。 ```rust trait Say<T> { fn hello(&self) -> (String, T); fn bye(&self) -> (String, T); } impl Say<i32> for Human { fn hello(&self) -> (String, i32) { (format!("Hello, {}", self.name), 1) } fn bye(&self) -> (String, i32) { (format!("Bye, {}", self.name), 2) } } ``` 上記の場合はほぼ変わりないが、異なる[[型引数 (Rust)|型引数]]の実装がある場合は、どの`Say`実装を使うのか指定しなければいけない。言い換えると、型ごとに異なる`Say`実装を2つ以上指定可能。 ```rust trait Say<T> { fn hello(&self) -> (String, T); fn bye(&self) -> (String, T); } impl Say<i32> for Human { fn hello(&self) -> (String, i32) { (format!("Hello, {}", self.name), 1) } fn bye(&self) -> (String, i32) { (format!("Bye, {}", self.name), 2) } } impl Say<String> for Human { fn hello(&self) -> (String, String) { (format!("Hello, {}", self.name), "one".to_string()) } fn bye(&self) -> (String, String) { (format!("Bye, {}", self.name), "two".to_string()) } } fn main() { let ichiro = Human::new("Ichiro".to_string()); eprintln!("ichiro.hello() = {:?}", ichiro.hello() as (String, i32)); eprintln!("ichiro.bye() = {:?}", ichiro.bye() as (String, i32)); } ``` [[関連型]]を使った場合は以下のようにはできない。[[Rustのトレイトは同じ型に対して複数回実装できない]]からだ。 ```rust trait Say { type Extra; fn hello(&self) -> (String, Self::Extra); fn bye(&self) -> (String, Self::Extra); } impl Say for Human { type Extra = i32; fn hello(&self) -> (String, Self::Extra) { (format!("Hello, {}", self.name), 1) } fn bye(&self) -> (String, Self::Extra) { (format!("Bye, {}", self.name), 2) } } // conflicting implementations of trait `Say` for type `Human` [E0119] impl Say for Human { type Extra = String; fn hello(&self) -> (String, Self::Extra) { (format!("Hello, {}", self.name), "one".to_string()) } fn bye(&self) -> (String, Self::Extra) { (format!("Bye, {}", self.name), "two".to_string()) } } ``` [[ジェネリクス (Rust)|ジェネリクス]]と[[関連型]]を含むケース。 ```rust use std::ops::Add; #[derive(Debug, PartialEq)] struct Millimeters(u32); #[derive(Debug, PartialEq)] struct Meters(u32); impl Add<Meters> for Millimeters { type Output = Millimeters; fn add(self, rhs: Meters) -> Self::Output { Millimeters(self.0 + (rhs.0) * 1000) } } fn main() { assert_eq!(Millimeters(1500) + Meters(2), Millimeters(3500)) } ``` `Say`[[トレイト]]の実装より、`Human`への直接実装の方が優先される。 ```rust trait Say { fn hello(&self) -> String; } struct Human { name: String, } impl Say for Human { fn hello(&self) -> String { format!("Hello {}", self.name) } } impl Human { fn hello(&self) -> String { format!("こんにちは {}", self.name) } } fn main() { let ichiro = Human { name: String::from("Ichiro"), }; // こんにちは eprintln!("ichiro.hello() = {:?}", ichiro.hello()); } ``` [[関連関数]]のケースも同じ。 ```rust trait Say { fn hello() -> String; } struct Human {} impl Say for Human { fn hello() -> String { String::from("Hello") } } impl Human { fn hello() -> String { String::from("こんにちは") } } fn main() { // こんにちは eprintln!("Human::hello() = {:?}", Human::hello()); } ``` 呼び出し方を変えると、`Human`に直接ではなく`Say`[[トレイト]]として実装された`Hello`を呼び出せる。[[フルパス記法]]。 ```rust fn main() { // Hello eprintln!("Human::hello() = {:?}", <Human as Say>::hello()); } ``` [[スーパートレイト]]。[[トレイト]]に制約をつけられる。以下の`HasName`[[トレイト]]は`Say`[[トレイト]]の[[スーパートレイト]]である。 ```rust trait HasName { fn get_name(&self) -> &str; } trait Say: HasName { fn hello(&self) -> String { format!("Hello {}", self.get_name()) } } struct Human { name: String, } impl HasName for Human { fn get_name(&self) -> &str { self.name.as_str() } } impl Say for Human {} fn main() { let ichiro = Human { name: "Ichiro".to_string(), }; eprintln!("ichiro.hello() = {:?}", ichiro.hello()); } ``` [[Rustにてニュータイプパターンで外部の型に外部のトレイトを実装]]する。[[Rustの外部トレイトは外部の型には実装できない]] のサンプルコードから。 ```rust use std::fmt::{Display, Formatter}; impl Display for String { // ここにエラーが出る fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) } } fn main() { println!("{}", String::from("hoge")); } ``` これを[[ニュータイプパターン]]で回避する。 ```rust use std::fmt::{Display, Formatter}; struct WString(String); impl Display for WString { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } fn main() { println!("{}", WString("hoge".to_string())); } ``` [[メソッド (Rust)|メソッド]]を委譲したくなければ[[Derefトレイト]]を使う方法がある。[[Rustのニュータイプパターンを利用しつつ、self.0を使わない方法]]。 ```rust use std::fmt::{Display, Formatter}; use std::ops::Deref; struct WString(String); impl Display for WString { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Deref for WString { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } } fn main() { let s = WString("hoge".to_string()); // true eprintln!("s.starts_with('h') = {:?}", s.starts_with('h')); // false eprintln!("s.starts_with('o') = {:?}", s.starts_with('o')); } ``` # 高度な型 - [[型エイリアス (Rust)|型エイリアス]] - あくまで同義語 - 定義の長い型を多くの場所で利用する場合に、間違いを防止するため使う - [[Result]]型は最たる例 - [[never型 (Rust)|never型]] - [[発散する関数]] 分かったような分からないような... ```rust fn main() { loop { let input = "15"; let guess: u32 = match input.trim().parse() { Ok(num) => num, Err(_) => continue, // continueはnever型(!)を返す // そのためguessはu32と断定できる }; eprintln!("guess = {:?}", guess); } } ``` [[panicマクロ]]が必ず実行される例は分かりやすいかも。 ```rust fn my_panic() -> ! { panic!("panic") } fn main() { // aは!に推論される let a = my_panic(); // Unreachable statement. Unreachable code. eprintln!("a = {:?}", a); } ``` - [[動的サイズ決定型]] - [[str]]は[[動的サイズ決定型]] - [[Sizedトレイト]] - [[Rustのコンパイラはすべてのジェネリックな関数にSizedトレイトを暗黙的に追加する]] - `T: ?Sized`という特別な[[トレイト境界]] `T`を[[Sizedトレイト]]を実装しているとは限らないとしたが、引数である`T`は[[借用]]が必須であるため、サイズを知る必要があり、[[Sizedトレイト]]を実装していないとコンパイルエラーになる。 ```rust use std::fmt::Display; // Help: function arguments must have a statically known size, borrowed types always have a known size fn generic<T: Display + ?Sized>(arg: T) { println!("{}", arg) } fn main() { generic(32); } ``` [[借用]]の発生しない`&T`であれば問題ない。 ```rust use std::fmt::Display; // Help: function arguments must have a statically known size, borrowed types always have a known size fn generic<T: Display + ?Sized>(arg: &T) { println!("{}", arg) } fn main() { generic(&32); } ``` # 高度な関数とクロージャ - [[関数ポインタ]] 以下の[[クロージャ (Rust)|クロージャ]]を返却するコードはエラーになる。 ```rust fn returns_closure() -> Fn(i32) -> i32 { |x| x + 1 } ``` ``` he trait bound `dyn Fn(i32) -> i32: std::marker::Sized` is not satisfied [E0277] `dyn Fn(i32) -> i32` does not have a constant size known at compile-time ``` [[クロージャ (Rust)|クロージャ]]には[[Sizedトレイト]]が実装されてなく、[[コンパイル]]時にサイズが確定しないから[[コンパイル]]できないためだ。[[Box]]などを使えば回避できる。 ```rust fn returns_closure() -> Box<dyn Fn(i32) -> i32> { Box::new(|x| x + 1) } fn main() { let xs = (1..=5).map(returns_closure()).collect::<Vec<_>>(); eprintln!("xs = {:?}", xs); // xs = [2, 3, 4, 5, 6] } ``` # マクロ ## [[マクロ]]と関数の違い - [[Rustの関数は可変長引数をとれないが、マクロは可変長引数をとれる]] - [[Rustの関数は定義の前に呼び出せるが、マクロは呼び出す前に定義する必要がある]] ## 一般的な[[メタプログラミング]]のためにmacro_rules!で宣言的なマクロ - [[宣言的マクロ]] - [[パターンマッチ (Rust)|パターンマッチ]]のよう ```rust macro_rules! kbys { ( $a:expr, $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); temp_vec.push($a); temp_vec.push($a); $( temp_vec.push($x); )* temp_vec } }; } fn main() { let xs = kbys![1, 2, 3]; eprintln!("xs = {:?}", xs); // [1, 1, 2, 3] } ``` ## [[属性 (Rust)|属性]]からコードを生成する[[手続き的マクロ]] - [[手続き的マクロ]] - 定義は専用の特殊な[[クレート]]内に置かれる必要がある - `_derive`のsuffixを持つ[[クレート]] - `hello_macro`[[クレート]]なら`hello_macro_derive`[[クレート]] - some_attribute[[属性 (Rust)|属性]] ```rust use proc_macro; #[some_attribute] pub fn some_name(input: TokenStream) -> TokenStream { } ``` ## カスタムの[[deriveマクロ]]の書き方 - [[Rustはリフレクション機能がない]] - 型の名前を[[ランタイム]]時に検索できないため、[[コンパイル]]時にコード生成が必要 - つまり[[マクロ]]がいる `hello_macro`ディレクトリの中で、`hello_macro_derive`を[[ライブラリクレート]]として作成する。`hello_macro_derive` ```console  ./hello_macro ├──  Cargo.lock ├──  Cargo.toml ├──  hello_macro_derive │ ├──  Cargo.toml │ └──  src │ └──  lib.rs └──  src ├──  lib.rs └──  main.rs ``` [[Cargo.toml]]はこんな感じ。 ```toml [package] name = "hello_macro_derive" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] syn = "1.0" quote = "1.0" ``` 1. [[proc_macro]]でソースコードを吸い上げて 2. [[syn]]で[[シンタックスツリー]]に変換し 3. ごにょごにょして 4. [[quote]]でソースコードに戻して 5. [[proc_macro]]で元に戻す [[syn]]でパースしたシンタックスツリーの中身。 ```rust DeriveInput { // --snip-- // 識別子 ident: Ident { ident: "Pancakes", span: #0 bytes(95..103) }, data: Struct( DataStruct { struct_token: Struct, fields: Unit, semi_token: Some( Semi ) } ) } ``` `impl_hello_macro`を実装する。 ```rust use proc_macro::{quote, TokenStream}; fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = quote! { impl HelloMacro for #name { fn hello_macro() { println!("Hello, Macro! My name is {}!", stringify!(#name)); } } }; gen.into() } ``` [[quote]]の[[マクロ]]に対してエラーが... ``` use of unstable library feature 'proc_macro_quote' [E0658] ``` [[proc_macro]]の`quote`を使っていたから。[[quote]]の`quote`を使うように変更する。 ```diff - use proc_macro::{quote, TokenStream}; + use proc_macro::TokenStream; + use quote::quote; ``` 準備は整ったので、これらを利用するプロジェクトを作成。 ```console cargo new pancakes ``` 最終的には以下のようになる。 ### pancakes `pancakes/Cargo.toml` ```toml [package] name = "pancakes" version = "0.1.0" edition = "2021" [dependencies] hello_macro = {path="../hello_macro"} hello_macro_derive = {path = "../hello_macro/hello_macro_derive"} ``` `pancakes/src/main.rs` ```rust use hello_macro::HelloMacro; use hello_macro_derive::HelloMacro; #[derive(HelloMacro)] struct Human {} fn main() { Human::hello_macro(); } ``` ### hello_macro `hello_macro/Cargo.toml` ```toml [package] name = "hello_macro" version = "0.1.0" edition = "2021" ``` `hello_macro/src/lib.rs` ```rust pub trait HelloMacro { fn hello_macro(); } ``` ### hello_macro_derive `hello_macro_derive/Cargo.toml` ```toml [package] name = "hello_macro_derive" version = "0.1.0" edition = "2021" [lib] proc-macro = true [dependencies] syn = "1.0" quote = "1.0" ``` `hello_macro_derive/src/lib.rs` ```rust use proc_macro::TokenStream; use quote::quote; #[proc_macro_derive(HelloMacro)] pub fn hello_macro_derive(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); impl_hello_macro(&ast) } fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = quote! { impl HelloMacro for #name { fn hello_macro() { println!("Hello, Macro! My name is {}!", stringify!(#name)); } } }; gen.into() } ``` ## [[属性風マクロ]] - 新しい[[属性 (Rust)|属性]]をつくることができる - [[deriveマクロ]]のように[[構造体 (Rust)|構造体]]と[[Enum (Rust)|Enum]]だけで使えるというわけではなく、関数などほかの要素にも利用できる ## [[関数風マクロ]] - [[macro_rulesマクロ]]を使った場合と似ている - 違いは`TokenStream`を引数にとること - [[パターンマッチ (Rust)|パターンマッチ]]ベースの[[macro_rulesマクロ]]よりも柔軟な処理が可能