[[📚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マクロ]]よりも柔軟な処理が可能