【Rust】個人的に注目中のUnstable Features（2022年1月）

最近、RustでUnstableな機能を使っているのですが、なんとなく雰囲気で使っているので、詳細をもう少し掘り下げて知ろうと思っていました。
そこで、ついでに最近こういうアウトプットを行っていなかったので、この機会にまとめを書こうと思います。

rustバージョンは、1.60.0-nightlyです。

# Unstable Featuresを使う

Unstable Featuresとは、まだ安定化されていない、Nightlyバージョンでのみ試験的に使用できるFeatureのことです。
[Unstable Book](https://doc.rust-lang.org/unstable-book/index.html)にその一覧が載っています。また、[RFCの一覧](https://rust-lang.github.io/rfcs/)も参考になるかと思います。

Unstable Featureを使うためには、まずnightlyバージョンのrustcをインストールします。
nightlyのインストールは[rustupのドキュメント](https://rust-lang.github.io/rustup/concepts/channels.html)に載っています（英語ですが）。

```shell
rustup toolchain install nightly
```

通常使うtoolchainをnightlyにするには

```shell
rustup default nightly
```

とします。

もし、通常はstableを使うけど、対象のプロジェクトやワークスペースのみnightlyにしたい場合は、プロジェクトのトップレベル(Cargo.tomlがある場所)に"rust-toolchain"というファイル名でファイルを作成し、その中に"nightly"という文字を書いておくと、`cargo build`等がnightlyで実行されるようになります。（この方法をお勧めします。）

そして、ソースのトップ(main.rsやlib.rs)に`#![feature(featureの名前)]`属性を付与すれば、そのプロジェクト全体で指定したfeatureが使えるようになります。

# 個人的に注目中のUnstable Features

言語仕様に関するFeatureをメインで挙げます。

## [generic_associated_types](https://doc.rust-lang.org/beta/unstable-book/language-features/generic-associated-types.html)

関連型にその関連型専用の型引数やライフタイム注釈を設定できるようになります。通称GATs。

```rust
#![feature(generic_associated_types)]

trait GatsTrait: Sized {
    type Gats<'a, T>
    where
        Self: 'a,
        T: 'a;

    fn gats_fn<'a: 'b, 'b, T>(
        &'a mut self,
        val: &'b mut T,
    ) -> (Self::Gats<'a, Self>, Self::Gats<'b, T>);
}

impl<U> GatsTrait for U {
    type Gats<'a, T>
    where
        Self: 'a,
        T: 'a,
    = Option<&'a mut T>;

    fn gats_fn<'a: 'b, 'b, T>(
        &'a mut self,
        val: &'b mut T,
    ) -> (Self::Gats<'a, Self>, Self::Gats<'b, T>) {
        (Some(self), Some(val))
    }
}
```
どういう意味があるかというと、これまでは関連型は、トレイトが実装されている型の型引数やライフタイムの関係を定義することは出来ましたが、トレイトのメソッドで定義されている型引数やライフタイムの関係を定義することは出来なかったのです。

上記の例はあまり実用性はありませんが、GATsの例としてよく、`StreamingIterator`\(`LendingIterator`\)や`Monad`が紹介されています。
ここでは`StreamingIterator`について触れてみます。

例えば、VecとそのVec内の要素を指すindexで構成されたIteratorを以下のように定義したいとします。

```rust
struct VecStruct<T> {
    vec: Vec<T>,
    index: usize,
}

impl<'a, T> Iterator for VecStruct<T> {
    type Item = &mut T;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(item) = self.vec.get_mut(self.index) {
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }
}
```
この`Item = &mut T`のライフタイムはどのように定義できるでしょう。ここのライフタイムは`next`メソッドの`&mut self`のライフタイムと同じです。しかし、メソッドで使用されているライフタイムを関連型で定義することは出来ません。そこで以下のような定義のイテレータを作成します。

```rust
#![feature(generic_associated_types)]

trait StreamingIterator {
    type Item<'a>
    where
        Self: 'a;
    fn next(&mut self) -> Option<Self::Item<'_>>;
}
```
この定義では、Itemにライフタイム注釈がついているため、Itemを使用する場所ごとに異なるライフタイムを使うことが出来ます。

```rust
impl<T> StreamingIterator for VecStruct<T> {
    type Item<'a>
    where
        Self: 'a,
    = &'a mut T;
    fn next(&mut self) -> Option<Self::Item<'_>> {
        if let Some(item) = self.vec.get_mut(self.index) {
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }
}
```

generic_associated_typesは[公式Blogでも推されているようなので](https://blog.rust-lang.org/2021/08/03/GATs-stabilization-push.html)、おそらく近いうちに安定化すると思われます。また、同じ関連型のfeatureとして、[associated_type_defaults](https://github.com/rust-lang/rust/issues/52662)、[associated_type_bounds](https://github.com/rust-lang/rust/issues/29661)も注目しています。

## [unboxed_closures](https://doc.rust-lang.org/beta/unstable-book/language-features/unboxed-closures.html), [fn_traits](https://doc.rust-lang.org/beta/unstable-book/library-features/fn-traits.html)

```rust
pub trait FnOnce<Args> {
    type Output;
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
pub trait FnMut<Args>: FnOnce<Args> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
```
クロージャの定義はこのようになっています。unboxed_closuresは、`extern "rust-call"`で関数定義を可能にし、fn_traitsは、クロージャを任意の型に実装できるようにします。

```rust
#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Adder {
    a: i32
}

impl FnOnce<(i32, )> for Adder {
    type Output = i32;
    extern "rust-call" fn call_once(self, b: (i32, )) -> Self::Output {
        self.call(b)
    }
}
impl FnMut<(i32, )> for Adder {
    extern "rust-call" fn call_mut(&mut self, b: (i32, )) -> Self::Output {
        self.call(b)
    }
}
impl Fn<(i32, )> for Adder {
    extern "rust-call" fn call(&self, b: (i32, )) -> Self::Output {
        self.a + b.0
    }
}
```

上記の`Adder`型は、クロージャとして振る舞うことが出来ます。

```rust
let adder = Adder { a: 2 };
assert_eq!(adder(2), 4);
assert_eq!([1, 2, 3, 4, 5].map(adder), [3, 4, 5, 6, 7]);
```

具体的な見える型に対して、クロージャを実装することができるので、トレイトにクロージャを返すメソッドを定義したり出来るようになるのが大きいかと思います。

## [unsized_fn_params](https://doc.rust-lang.org/beta/unstable-book/language-features/unsized-fn-params.html)

関数の引数にサイズ不定の変数を渡せるようになります。主に`dyn Trait`のためのfeatureだと思っています。

本来、`Self`をパラメータに持つトレイトは、トレイトオブジェクトに出来ないのですが（Sizedではないため、パラメータに出来ない）、`FnOnce`は普通に`Box<dyn FnOnce>`として扱えます。これにはトリックがあって、

```rust
impl<Args, F: FnOnce<Args> + ?Sized, A: Allocator> FnOnce<Args> for Box<F, A> {
    type Output = <F as FnOnce<Args>>::Output;

    extern "rust-call" fn call_once(self, args: Args) -> Self::Output {
        <F as FnOnce<Args>>::call_once(*self, args)
    }
}
```

という定義があり、これにより、`Box`が直接`FnOnce`を実装する、`Self`(`Box`)が`Sized`になるので、`Box`で包んでいる限り、`dyn FnOnce`が扱えます。

ただし、本来は`*self`がSizedではないため、上記のような定義はコンパイルエラーとなります。なので、この定義を可能にするため、coreライブラリには`#![feature(unsized_fn_params)]`が設定されており、サイズ不定の引数を扱えるようになっています。

もう少し一般的なトレイトでやってみると、以下のようになります。

```rust
#![feature(unsized_fn_params)]

trait MyTrait {
    fn my_proc(self);
}

impl<F> MyTrait for Box<F>
where
    F: MyTrait + ?Sized,
{
    fn my_proc(self) {
        (*self).my_proc();
    }
}
```

このような感じで、`Self`をパラメータに持つトレイトをトレイトオブジェクトとして扱えるという点で注目しています。

また、似たようなfeatureで[unsized_locals](https://doc.rust-lang.org/beta/unstable-book/language-features/unsized-locals.html)がありますが、こちらはまだincompleteとなっています。

## [generators](https://doc.rust-lang.org/beta/unstable-book/language-features/generators.html), [generator_trait](https://doc.rust-lang.org/beta/unstable-book/library-features/generator-trait.html)

途中で中断、再開できる関数を作成できるようになります。他の言語でもよくある、`yield`を使えるようになります。

```rust
#![feature(generators, generator_trait)]

fn gen<F>(iter: F) -> impl Generator<i32, Yield = i32, Return = &'static str>
where
    F: Iterator<Item = i32>,
{
    move |mut i: i32| {
        for v in iter {
            i = yield v + i;
        }
        "done"
    }
}

let mut generator = gen([1, 2, 3, 4, 5].into_iter());

let mut buf = 0;
loop {
    match Pin::new(&mut generator).resume(buf) {
        GeneratorState::Yielded(i) => {
            print!("{} ", i);
            buf += i;
        }
        GeneratorState::Complete(s) => {
            print!("{}", s);
            break;
        }
    }
}
// 1 3 7 15 31 done
```

`Generator`トレイトは、[すでにライブラリドキュメントが用意されているので](https://doc.rust-lang.org/core/ops/trait.Generator.html)、そちらを参照してください。
現状ではこれだけの機能ですが、今後、[stream](https://doc.rust-lang.org/std/stream/index.html)などと組み合わせて非同期で楽に使えるような仕様になれば、非常に便利になると思います。

また、似たような機能を実現するライブラリで、[tokioのasync-stream](https://github.com/tokio-rs/async-stream)もあります。

## [specialization](https://doc.rust-lang.org/beta/unstable-book/language-features/specialization.html), [min_specialization](https://doc.rust-lang.org/beta/unstable-book/language-features/min-specialization.html)

あるトレイトの実装を、汎用の実装と特化した実装に分けて実装できるようになります。

specializationは、結構前からrfcに上がっているのですが、仕様がまだ固まっていないようで、incompleteとなっています。しかし、その一部だけでも標準ライブラリで使用するため、min_specializationというサブセットが作られました。昨年に安定化した[min_const_generics](https://github.com/rust-lang/rust/pull/79135)と似たような流れなのでしょうか。

```rust
#![feature(min_specialization)]

trait DebugString {
    fn debug_string(&self) -> String;
}

// 1. 一般的な型に実装する
impl<T> DebugString for T
where
    T: Debug,
{
    // default
    default fn debug_string(&self) -> String {
        format!("{:?}", self)
    }
}

// 2. 1について特化した型に実装する
impl DebugString for i32 {
    // specialized
    fn debug_string(&self) -> String {
        format!("{:#?}", self)
    }
}
```

上記のように、まず、一般的な型についてトレイトを実装します。このとき、`fn`の前に`default`というキーワードをつけます。そして、それを特化した実装を`default`キーワード無しで実装します。

残念ながら、min_specializationでspecializationのどこまでの範囲が使えるようになるのかよくわからなかったので、これ以上説明が出来ません。

min_specializationに関しては、すでに標準ライブラリで使用されているので、気になる方はチェックしてみてください。現在、`ToString`の実装等で使われています。

## [try_block](https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html)

`?`演算子のスコープとなる`try`ブロックを作成できるようになります。

`?`演算子は、エラーを受け取ると早期returnで関数を抜けるため、関数内で`?`演算子を使いつつ`Result`や`Option`の処理を行いたい場合は

```rust
let result: std::io::Result<String> = (|| {
    let mut file = File::open("foo.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
})();
```

のように、クロージャでスコープを作る必要がありました。これが、`try`ブロックが使えるようになると

```rust
#![feature(try_block)]

let result: std::io::Result<String> = try {
    let mut file = File::open("foo.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    contents
};
```

のように、クロージャを作らずに書くことが出来ます。

`try`ブロック内の`return`は`try`ブロックを通り越して関数を抜けるので、クロージャを使うよりも扱いやすくなります。
もともと`?`演算子とセットで`?`のスコープブロックも提案されていたようですが、`?`演算子が先に安定化されて、`try`ブロックは諸々仕様を議論して今に至っているようです。

また合わせて、`?`演算子が使える型を定義できる[try_trait_v2 feature](https://github.com/rust-lang/rust/issues/84277)も便利そうです。

# 最後に

Unstable Featureを知っていると、もちろんそれを使うという意味でも役立つのですが、Stableで今できないことがわかるので、余計な試行錯誤せずに済んだりします。ここに上げたFeatureは、そこそこメジャーなものだと思いますので、頭の片隅においておくといつか役立つかもしれません。

また、ここに載せた情報は、あまり正確な情報が見つからないものが多かったので、私が検証して想像したものも含まれています。誤った情報があれば、ご報告いただければ修正します。