Rust大好きな自分が、あえてRustの嫌いな所を挙げてみた

あえて言おう。**Rustが大好きである**と。

この記事は[Rust 2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/rust2) の8日目の記事です。
近年、Rustの人気が上がってきているようで、インターネット上でも、「Rust 好き」等で検索するとさまざまな記事が見つかる反面、逆に「Rust 嫌い」というワードで出る記事が少ないのが不公平な気もするので、あえてRustの良くないなと思う点を挙げてみました。

# 非同期処理について

Rustには[ビルトインの非同期処理](https://tech-blog.optim.co.jp/entry/2019/11/08/163000)があります。これは大きく分けて2つの要素から成り立っています。

1. [`Future`](https://doc.rust-lang.org/std/future/trait.Future.html)トレイト
2. ランタイム

詳しくは他の記事を参照していただきたいのですが、あまり正確でない説明をすると、**1. の`Future`クレイト**は非同期処理自体を定義するためのトレイトです。例えば、Rustの組み込みキーワード`async`で定義された関数

```rs
async fn func() -> Result<(), Error> { /* snip */ }
```

は、

```rs
fn func() -> impl Future<Output = Result<(), Error>> { /* snip */ }
```

ような感じに変換されます。この時`Future`トレイトが使われています。しかしながら、ここで`func()`を

```rs
let _ = func();
```

としても、`func()`の中身は実行されません。`Future`トレイトを実装したオブジェクトが生成されるだけです。
実際に`Future`の処理を実行するためには、**2. のランタイム**が必要であり、これは`Future`トレイトと違ってRustの標準ライブラリには含まれません。
よって、ランタイムを実装した外部トレイトに依存する必要があります。

現在、Rustで広く使われているランタイムには(私が知っている限り)以下のものがあります[^1]。

- `tokio` 0.2系
- `tokio` 0.3系
- `tokio` 1.0系 (←追加)
- `futures`

例えば、`tokio` 0.2系で作られた`Future`を`tokio` 0.3系や`futures`のランタイムで実行することは基本的にできません。
これが特に問題となるのは`async fn`を含む外部クレートを利用する場合で、その外部クレートがサポートするランタイムがどれかを利用者が注意深く調べておく必要があります。`tokio` 0.3系のランタイムを使用したくても、依存するクレートが`tokio` 0.2系に依存している場合は、0.3系を使うことができません。

現在は移行期であり、今後`tokio` 0.3系に対応するクレートが増えてくると思いますが、完全に移行し切るまでは、クレートのインストール時に動で`features`を指定する等する必要があるでしょう。

[^1]: `async-std`は現在`futures`を部的に使っているのと思われます。また`actix-rt`は`tokio`を使っているものと思われます。


# 使えるトレイトが多くて混乱する

トレイトは、Rustのすばらしい機能の一つです。逆に、Rustの機能の多くはトレイトに依存しているので、トレイトを使いこなせなければRustを便利に活用することは難しいでしょう。
しかし、トレイトには多くの種類があるため、トレイトの全貌を知るのは簡単ではありません。特に、似たような機能が複数のトレイトで提供されている場合があり、ドキュメントを読んでもどう使い分ければいいか理解できないことが多いです。例えば、以下のトレイトはすべて「型の変換」に係るトレイトです。

- `From`
- `Borrow`
- `AsRef`
- `ToOwned`
- `Deref`

この中で、`Deref`はスマートポインタに対して演算子`*`を適用したときの動作をオーバーライドするトレイトであり、使用には注意が必要です。
`Borrow`と`AsRef`については以下を参照。

[Borrow and AsRef](https://qiita.com/hibariya/items/b24f893f88d0dc931c61)

# 関数の引数と所有権

情報提供: [@Iwancof_ptr](https://twitter.com/Iwancof_ptr)さん

例えば以下のコードはエラーになります。なぜでしょうか?

```rs
struct S;

impl S {
	pub fn outer_ref(&self, _: i32) {
	}
	pub fn outer_mut(&mut self, _: i32) {
	}
	pub fn inner_ref(&self) -> i32 {
		10
	}
	pub fn inner_mut(&mut self) -> i32 {
		10
	}
	
}
 
fn main() {
	let mut s = S;
	s.outer_ref(s.inner_mut());
}
```

ここで、`outer_ref()`呼び出しを以下のように展開してみるをわかりやすいと思います。

```rs
S::outer_ref(&s, S::inner_mut(&mut s));
```

これを更に分解するとこのようになります。

1. 変数`s`の参照`&s`を取得する。
2. 変数`s`の参照`&mut s`を取得する。
3. `&mut s`を引数として`S::inner_mut()`を呼び出し値`10`を得る。
4. `&s`及び`10`を引数として`S::outer_ref()`を呼び出す。

ここで、2. から3. までの間、変数`s`に対する参照`&s`及び`&mut s`が共存しています。これがエラーの原因だと思われます。
よって、以下のように書けばエラーは無くなります。

```rs
fn main() {
	let mut s = S;
	let i = s.inner_mut();
	s.outer_ref(i);
}
```

また、試したところ`s.outer_mut(s.inner_ref())`はエラーになりませんでした。原因がわかる方教えてください。(追記: [`Two-phase borrow`によるものとの事](https://qiita.com/yasuo-ozu/items/f8e58a06e327b6dbb62d#comment-cd8a803e244016a8189b)です。)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://t.co/rvZq7Mj6T0">https://t.co/rvZq7Mj6T0</a><br><br>こんな感じになりました。<br>うーん。どちらかを mut にすると借用できないっぽいですね。</p>&mdash; いわんこ (@Iwancof_ptr) <a href="https://twitter.com/Iwancof_ptr/status/1330159893291134987?ref_src=twsrc%5Etfw">November 21, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# (追加) メソッド呼び出し構文のaudo-dereferenceについて

[参考](https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules)

Rustでは、型`X`のオブジェクト`x`に対してメソッド`m`を呼び出す際に、`X::m(x)`もしくは`x.m()`という構文を用いることができます。しかし、後者の場合、自動Deref機能が働き、以下の例のように本来`(&x).m()`や`(*x).m()`と書くべきところで`*`や`&`を省略することができます。

```rs
// ---- & の例 ----
struct X;
impl X {
    fn m(&self) {}
}
let x = X;
X::m(&x);
(&x).m(); // 上に同じ
// ここで&は下のように省略できる
X.m();
// ---- * の例 ----
let y = Box::new(1.5f64);
println!("{}", f64::ceil(*y));
println!("{}", (*y).ceil()); // 上に同じ
// ここで*は以下のように省略できる
println!("{}", y.ceil());
```

これは便利な機能ですが、時折互換性の問題を起こすという問題があります。

例えば、[`IntoIterator`](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html)というトレイトがあります。これは、`vec![1,2,3].into_iter()`のように`into_iter()`という関数を提供してイテレータを生成するためのトレイトです。これを配列`[T; N]`に実装しようという議論があります。

[Add `IntoIterator` impl for arrays by value (`for [T; N]`)](https://github.com/rust-lang/rust/pull/65819)

しかし、ここで問題なのは、すでに`&[T; N]`に対して`IntoIterator()`が実装されている点です。すなわち、以下のようなコードは現時点でも合法[^2]で、`<&[i32; 3] as IntoIterator>::into_iter(&arr)`がよばれます。そのため、ループ内で使える変数`i`は`&i32`型です。

```rs
let arr = [1, 2, 3i32];
for i in arr.into_iter() {
    println!("{}", i);  // i は &i32
}
```

一方、`[T; N]`に対して`IntoIterator`を実装すると、上のコードでは代わりに`<[i32; 3] as IntoIterator>::into_iter(arr)`が呼ばれることになり、変数`i`の型は`i32`となります。これは明らかに互換性を侵害しています。

実際、上記コードをコンパイルすると以下のような警告が表示されます。

```text
  |
3 |     for i in arr.into_iter() {
  |                  ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
  |
  = note: `#[warn(array_into_iter)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
```

現状の最新のエディションは`edition=2018`ですが、近い将来のエディションでは解消されるのではないでしょうか。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/rustlang?src=hash&amp;ref_src=twsrc%5Etfw">#rustlang</a> [T; N] に IntoIterator&lt;Item=T&gt;を実装してほしい。<br>&amp;[T; N]に対するIntoIter&lt;Item=&amp;T&gt;はすでに実装されてるけど。</p>&mdash; yasuo_ozu@FizzBuzzむずい (@yasuo_ozu) <a href="https://twitter.com/yasuo_ozu/status/1343590001422942209?ref_src=twsrc%5Etfw">December 28, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[^2]: `&[T]`にも`IntoIterator`が実装されているため、これに合わせて`&[T; N]`にも実装しているのだと思います