組込みエンジニアのためのRustのポインタ・参照・キャストまとめ

# はじめに
どうも、レガシー組込みエンジニアの@yagisawaです。
最近(と言いたいところですが2年前から)細々と組込みRustにチャレンジしているのですが、Rustのポインタの扱いがなかなかCのようにいかなかったので調べたことをまとめました。

解説に使うコード例は[The Embedonomicon - A main interface](https://docs.rust-embedded.org/embedonomicon/main.html)にある以下を扱います。

```Rust
extern "C" {
    static mut _sbss: u8;
    static mut _ebss: u8;

    static mut _sdata: u8;
    static mut _edata: u8;
    static _sidata: u8;
}

let count = &_ebss as *const u8 as usize - &_sbss as *const u8 as usize;
ptr::write_bytes(&mut _sbss as *mut u8, 0, count);

let count = &_edata as *const u8 as usize - &_sdata as *const u8 as usize;
ptr::copy_nonoverlapping(&_sidata as *const u8, &mut _sdata as *mut u8, count);
```

上記コードが何を意味しているかについては拙作の以下の記事をご参考ください。

https://qiita.com/yagisawa/items/addb3ab16a677e597e44#%E5%88%9D%E6%9C%9F%E5%80%A4%E4%BB%98%E3%81%8D%E5%A4%89%E6%95%B0%E3%81%AE%E5%88%9D%E6%9C%9F%E5%8C%96

# ポインタ(アドレス)を数値にキャストする

組込みソフトではアドレスを数値として扱って加減算したいことがあります。コード例のように`bss`セクションの終了アドレスから開始アドレスを引いて`bss`セクションのサイズを求めたいとき等です。

Cでは

```C:C
unsigned int count = (unsigned int)&_ebss - (unsigned int)&_sbss;
```

と直接ポインタを`unsigned int`にキャストできますが、Rustでは

```Rust:Rust
let count = &_ebss as *const u8 as usize - &_sbss as *const u8 as usize;
```

のようにしなければならないようです。
わかりやすいように括弧を付けると

```Rust:Rust
let count = ((&_ebss as *const u8) as usize) - ((&_sbss as *const u8) as usize);
```

となり、`&_ebss`を一旦`*const u8`にキャストしてから更に`usize`にキャストしています。

試しに

```Rust:Rust
let count = (&_ebss as usize) - ((&_sbss as *const u8) as usize);
```

としてみたところ

```
error[E0606]: casting `&u8` as `usize` is invalid
  --> src\main.rs:44:17
   |
44 |     let count = (&_ebss as usize) - ((&_sbss as *const u8) as usize);
   |                 ^------^^^^^^^^^^
   |                 ||
   |                 |help: dereference the expression: `*&_ebss`
   |                 cannot cast `&u8` as `usize`

For more information about this error, try `rustc --explain E0606`.
```

めっちゃ怒られました。「\`&u8\`を\`usize\`にキャストするのは無効です」と言っています。
[Type cast expressions - The Rust Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions)に記載されている通り、キャストには規則があり`&T`から`Integer type`にはキャストできません。
そこで`&T -> *T`規則と`*T -> Integer type`規則を使ってキャストしているわけです。

また、`*T`は`*const T`か`*mut T`だよと書かれているので

```Rust:Rust
let count = ((&mut _ebss as *mut u8) as usize) - ((&mut _sbss as *mut u8) as usize);
```

と書いても大丈夫です。
が、欲しいのはアドレスなので`_ebss`のポインタがミュータブルになっても特に嬉しくありません。`const`で十分です。

余談ですが、欲しいのはアドレスなので(2回目)

```Rust:Rust
let count = ((&_ebss as *const _) as usize) - ((&_sbss as *const _) as usize);
```

のように、ポインタの型を省略することもできるようです。

余談ですが(2回目)、Rustでは&(参照)と*(ポインタ)は別物として扱われているようです。
そのため、Cではあり得ない

```Rust:Rust
let reference = &4;
```

なんてコード(4のポインタ？ではなく、4という数値が格納されているどこかの領域への参照)が書けてしまいます。[ここ](https://doc.rust-jp.rs/rust-by-example-ja/flow_control/match/destructuring/destructure_pointers.html)に書かれていた例なのですが、初めて見たときは目ん玉飛び出ました。

# 暗黙の型変換(型強制)

コード例にある`ptr::write_bytes`の引数は

```Rust:Rust
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize)
```

のようになっています。Cの`memset`のような関数です。
Cでは特に型を意識せずに

```C:C
int a[10];
memset(a, 0, sizeof(a));
```

とかやっているのではないでしょうか？
`memset`の引数は

```C:C
void *memset(void *buf, int ch, size_t n);
```

ですが、`void *buf`に`int *`を渡しています。

Rustでも試しに

```Rust:Rust
ptr::write_bytes(&_sbss, 0, count);
```

としてみたところ

```
error[E0308]: mismatched types
    --> src\main.rs:45:17
     |
45   |     write_bytes(&_sbss, 0, count);
     |     ----------- ^^^^^^ types differ in mutability
     |     |
     |     arguments to this function are incorrect
     |
     = note: expected raw pointer `*mut _`
                  found reference `&u8`
note: function defined here
    --> C:\Users\user\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library/core/src/intrinsics.rs:2402:21
     |
2402 | pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
     |                     ^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
```

めっちゃ怒られました(2回目)。「生ポインタ\`*mut _\`を期待しているが、\`&u8\`参照が見つかりました」と言っています。

次に「ミュータブルじゃないと書込みできないよねー」ということで

```Rust:Rust
ptr::write_bytes(&mut _sbss, 0, count);
```

と**ミュータブルな参照**にしてみたところ…**ビルドが通りました？！**
どういうことかというと、[Type coercions - The Rust Reference](https://doc.rust-lang.org/reference/type-coercions.html#coercion-types)に記載されている通り、特定の状況で暗黙の型変換が行われます。
今回の場合は`&mut u8 -> *mut T`なので`&mut T -> *mut T`に該当します。

# おわりに

知識が浅すぎてうまくまとめられませんが、まとめると

* Rustのキャストには規則がある
* Rustは`&`と`*`は区別する
* Rustにも暗黙の型変換はある

といったところでしょうか。
うまく使いこなして(あまりキャストを使わず？)Rustらしいコードを書きたいところです。できればポインタの使用は最小限に抑えたいですね。

色々実験していてエラーを出しまくりましたが、Rustのエラーは親切だなと感じました。
例えば``cannot cast `&u8` as `usize` ``については``For more information about this error, try `rustc --explain E0606`.``との記載があり、実行してみると

````
An incompatible cast was attempted.

Erroneous code example:

```
let x = &0u8; // Here, `x` is a `&u8`.
let y: u32 = x as u32; // error: casting `&u8` as `u32` is invalid
```

When casting, keep in mind that only primitive types can be cast into each
other. Example:

```
let x = &0u8;
let y: u32 = *x as u32; // We dereference it first and then cast it.
```

For more information about casts, take a look at the Type cast section in
[The Reference Book][1].

[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
````

と例が表示されます(期待した解決策ではありませんでしたが…)。
更に`[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions`との記載があり、何故エラーになったか(どう修正すればよいか)がわかった、というわけです。