Rust 1.45.0の新機能


## はじめに

2020年7月16日にRust 1.45.0がリリースされました。途中にパッチリリースの1.44.1が挟まりましたが、だいたい6週間ごとのリリースサイクルが守られているようです。この記事では[Announcing Rust 1.45.0](https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html)の内容を基に1.45.0の新機能を確認してみたいと思います。

なお、Rustのインストールやアップデートをしてくれる Rustupも7月8日に[1.22.1になっている](https://blog.rust-lang.org/2020/07/08/Rustup-1.22.1.html)ので合わせてアップデートすると良いかと思います。以下でサクッと上がると思います。

```sh
$ rustup self update
```

そのうえで、

```sh
$ rustup update stable
```

すれば 1.45.0が使えるようになります。

## 新機能

### 型変換の問題の修正

これは新機能というより問題の修正ですが、以前のRustだとCastする際に値の範囲を越えると未定義の動作になる（つまりどうなるかわからない）という問題がありました。例えば、以下のような関数を考えます。これは32ビット浮動小数点型の値を8ビットの符号なし整数型に変換するものです。

```rust
pub fn cast(x: f32) -> u8 {
    x as u8
}
```

出力が8ビットの符号なし整数型なので取りうる値は0から255までで、それをそれを越えると何がおこるかわからない :cry:  これはRustのコンパイラバックエンドで使っている LLVMがそういうコードを出力するからですが、よろしくないですよね。この問題自体はRustが1.0になるだいぶ前の2013年からあって（7年前！)、でもここまで解決されずに残っていました。

上記の関数を以下のように使ってみます。300.0 は許容範囲の 255 を越えているので`x`に何が入るかはわかりません。私の場合は `x: 44`が表示されましたが、人によって異なる値が表示されるはずです。

```rust
fn main() {
    let f = 300.0;
    let x = cast(f);
    println!("x: {}", x);
}
```

コンパイル時にエラーにならず、実行時にもこれが起きていることがわからない。厄介ですね。こういうのがスルーされるのはRustっぽくないです。

で、このバグには「cast soundness」バグという名前が付けられていました。「型変換の不健全」バグという感じでしょうか。名前をつけられていつか直すものとして追いかけられていましたが、どう直すのが良いかが明確ではなかったので修正には時間がかかりました。

最終的に以下の結論に至り、今回の1.45.0で修正されました。

* `as`は saturation cast (飽和型変換)を行う
  * 型変換先の値の範囲を超える場合にはその最大値あるいは最小値に変換する
* この動作をさせたくない時には `unsafe`型変換を使う

これは配列で`array[i]`とした場合は配列が`i+1`個の要素を持っていることをチェックし、そのチェックを外したい時には `unsafe`キーワードを使うのと似ています。

1.45.0で以下のコードを実行してみると動作がわかると思います。

```rust
fn main() {
    println!("{}", cast(300.0));     // --> 255
    println!("{}", cast(-100.0));    // --> 0
    println!("{}", cast(f32::NAN));  // --> 0
}
```

値が型変換先の範囲に入っていることがわかっているので、余計なチェックをさせたくない場合には `unsafe`をつかって以下のようにできます。

```rust
let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };
```

速度重視の場合にはありかも知れませんが、コンパイラがよろしくやってくれる場合もあり、あまりオススメではないようです。

### 関数的手続きマクロの利用可能場所が増える

まず今回の変更の前に手続きマクロについて簡単に書いておきます。

Rust 1.30.0で手続きマクロが導入されました。これは、`TokenStream` （Rustコードの断片）を入力とし`TokenStream`を出力とするマクロです。例えば、以下のような手続きマクロを考えてみます。

```rust
use proc_macro::TokenStream;

#[proc_macro]
pub fn make_func(item: TokenStream) -> TokenStream {
    format!("fn func() -> u32 {{ {} * 2 }}", item)
        .parse()
        .unwrap()
}
```

これは `func()`という関数定義を返すマクロですが、`item`で指定した式に`*2`した値を返す関数です。

使う側では

```rust
use func_proc_macros::make_func;

make_func!(1 + 2);

fn main() {
    println!("{}", func());
}
```

といった形で使えます。これを実行すると `5`が結果として出力されます。これを理解するのにマクロがどのように展開されているかを確認してみます。

```sh
$ cargo install cargo-expand
$ cargo expand
```

を実行すると、`make_func!(1 + 2)`が以下のように展開されていることがわかります。

```rust
fn func() -> u32 {
    1 + 2 * 2
}
```

これを見ると確かに返り値は`5`になりますね。`item * 2`なので一瞬答えは `6`かなと思うのですが、式を文字列で貼り付けているだけなので `1 + 2 * 2`となり、掛け算が先に行われ答えは `5`になります。

この機能は以前のバージョンから利用可能でしたが、使える場所が限られていました。上記のように、トップレベルの item position というところにしか置けませんでした。これが、Rust 1.45.0からは他のところにも置けるようになりました。具体的には以下の場所が新たに追加されました。

* expression position: 式が使われる場所
* pattern position: `match`でパターンマッチングが行われる場所
* statement position: 文が使われる場所

```rust
fn main() {
  let expr = mac!(); // expression position

  match expr {
      mac!() => {} // pattern position
  }

  mac!(); // statement position
}
```

これまでは item positionにしか置けなかったので関数定義などトップレベルに置けるものしかマクロ化できませんでしたが、これからは式や文なども手続きマクロを使って書くことができるようにになり応用範囲がぐっと広がるのではないかと思います。

なお、この機能を使った面白い実例が[Rustで+++を実現する](https://qiita.com/dalance/items/18c5f5bb5ec643ac9aec)に載せられています。ご興味のある方はぜひそちらもどうぞ。

### charをレンジで使えるようになる

文字型をレンジで使えるようになりました。以下を実行すると`abcdefghijklmnopqrstuvwxyz`が出力されます。

```rust
for ch in 'a'..='z' {
    print!("{}", ch);
}
println!();
```



## まとめ

Rust 1.45.0の新機能をまとめてみました。Pythonは1年に一度のリリース（それでも[短くなった](https://qiita.com/ksato9700/items/a6578b09b2949c8548dd)）ですが、それに比べると6週間に一度というのは随分と短くその分変更点もコンパクトになりますね。これからも気になる新機能が出てきたら書いてみたいと思います。