Rust と絵文字 🦀

# はじめに
Rust に限らず、テキスト処理をする際に注意しなければならないのが「**絵文字**」です。

本記事では、`Unicode` と `UTF-8` の基礎と、Rust で絵文字を扱う際の注意点を解説します。

# Unicode と UTF-8
Rust は**文字コード**として **Unicode**、**符号化方式**として **UTF-8** を採用しています。 一方で、JavaScript は**符号化方式**として **UTF-16** を採用しています。

簡単に説明すると、

- **文字コード**は、ひとつひとつの文字に識別番号を振るときのルール
- **符号化方式**は、その識別番号をコンピュータのメモリ上に実際に配置するときのルール

です。

プログラム言語によって、文字コードや符号化方式が異なることに注意が必要です。



## 文字コード
**文字コード**は、コンピュータ上で文字を扱うために、文字に番号を割り振って定義したものです。`ASCII` や `Unicode`、`Shift_JIS` などの種類があります。近年では **Unicode** が主流です。

**Unicode** は、世界中のあらゆる言語で使用されるすべての文字を、固有の**識別番号**で管理する国際規格です。この識別番号のことを**コードポイント**と呼びます。
例えば、`a` の**コードポイント**は 16 進数表記で `61` です（10 進数表記: `97`）。


Rust で文字列を記述する際、通常の文字表現だけでなく、コードポイントを用いて記述する方法があります。`\u{NNNNNN}` のように Unicode のコードポイントを直接記述する方法です。`NNNNNN` には、表現したい文字のコードポイントを 16 進数で記述します。

例えば、`"a"` と記述する代わりに `"\u{0061}"` と記述することが可能です。
```rust
let a = "\u{0061}";
println!("{}", a);
```

## 符号化方式
**符号化方式**は、**コードポイントをビット列に変換**する方法です。コンピュータ上では、あらゆるデータが 2 種類の電気信号 ( `0` と `1` に対応 ）で表現されています。そのため、コンピュータ上で文字を扱う際にはコードポイントをビット列に変換する必要があります。

符号化方式には `ASCII` や `UTF-8`、`UTF-16` などの種類があります。（`ASCII` は文字コードと符号化方式の両方を含む仕様です。）

**UTF-16** はメモリ上で 1 文字を **16 ビット単位で表現**する符号化方式です。UTF-16 は**サロゲートペア**という独自の仕組みに注意が必要です。Rust には UTF-16 はあまり関係ないので説明しませんが、詳しくは以下の記事で参照してください。

https://qiita.com/shikuno_dev/items/4a4f050de6bd4c819a51

一方、Rust で採用されている **UTF-8** はメモリ上で 1 文字を **1 ~ 4 バイトの列で表現**する符号化方式です。1 文字あたりのバイト数は、コードポイントによって異なります。


| メモリ上（1 ~ 4 バイト）  | コードポイント    | 範囲                  |
| -------------------------------------------------- | ----------------------- | --------------------- |
| 0xxxxxxx                                           | 0bxxxxxxx               | 0 ~ 0x7f           |
| 110xxxxx 10yyyyyy                                  | 0bxxxxxyyyyyy           | 0x80 ~ 0x7ff       |
| 1110xxxx 10yyyyyy 10zzzzzz                         | 0bxxxxyyyyyyzzzzzz      | 0x800 ~ 0x7ffff    |
| 11110xxx 10yyyyyy 10zzzzzz 10wwwwww                | 0bxxxyyyyyyzzzzzzwwwwww | 0x10000 ~ 0x10ffff |

 ※ UTF-8 では `0xd800` から `0xdfff` までの範囲（サロゲートペア）と `0x10ffff` より大きい値のコードポイントは、ビット列に変換できません。


# 絵文字
**絵文字**（emoji）は、テキストメッセージや Web ページなどで広く使用されている小さな図形（例: 😡, 🐻‍❄️, 🇺🇸）です。これらは感情や動物、天気などを表現するために使われ、コミュニケーションを豊かにします。


## 絵文字の構造
絵文字は Unicode 標準に基づいて定義されており、単一コードポイントのものもあれば、複数のコードポイントを組み合わせたものも存在します。

- **単一コードポイント**の絵文字: 😊 (`U+1F60A`)
- **複数コードポイント**の組み合わせ: 👩‍👩‍👧‍👦 (`U+1F469 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466`)

👩‍👩‍👧‍👦 は **ZWJ** (Zero Width Joiner) と呼ばれる特殊な文字 (U+200D) を用いて結合されています。ZWJ によって、独立した絵文字が一つの絵文字として見えるようになります。

## 基本的な絵文字の扱い
Rust では、絵文字は通常の `&str` や `char` として扱われます。

```rust
let emoji = "😊"; // 絵文字を文字列として定義
println!("Emoji: {}", emoji);

let char_emoji: char = '😊'; // 絵文字を文字として定義
println!("Emoji as char: {}", char_emoji);

// \u{NNNNNN} 形式で記述することも可能
let smile_emoji = "\u{1F60A}"; // 😊
```

`char` 型は絵文字のコードポイントも簡単に取得できます。
```rust
let emoji = '😊';
println!("Code point: U+{:X}", emoji as u32); 
// 出力: Code point: U+1F60A
```
複数のコードポイントを組み合わせた絵文字（例: "👩‍❤️‍💋‍👩"）は、Rust では文字列 `&str` として扱われます。絵文字を構成する各コードポイントを確認するには、chars メソッドを使用します。
```rust
let emoji = "👩‍❤️‍💋‍👩";
println!("Emoji: {}", emoji);

for c in emoji.chars() {
    println!("Code point: U+{:X}", c as u32);
}
// 出力: 
// Code point: U+1F469
// Code point: U+200D
// Code point: U+2764
// Code point: U+200D
// Code point: U+1F48B
// Code point: U+200D
// Code point: U+1F469
```

## Rust での絵文字処理
Rust では、絵文字を含む文字列を扱う際に以下の点に注意する必要があります。


**文字数のカウント**
  Rust の `len` メソッドはバイト数を返しますが、絵文字のように 1 文字が複数バイトで構成される場合、**視覚的に**正確な文字数ではありません。
```rust
let s = "😊";
println!("{}", s.len()); // 出力: 4 (バイト数)
println!("{}", s.chars().count()); // 出力: 1
let t = "👩‍❤️‍💋‍👩";
println!("{}", t.len()); // 出力: 27(バイト数)
println!("{}", t.chars().count()); // 出力: 8
```
`chars().count()` で文字数を確認すると、**単一コードポイントの絵文字**では視覚的な文字数と一致します。

`ZWJ` を使用した方法以外にも以下のような、複数のコードポイントを組み合わせた絵文字があります。

- 2 つの文字を並べて国旗を表現する `REGIONAL INDICATOR`（例: "🇺🇸" <- 🇺と🇸）
- 肌の色を変更する `Emoji Modifier Fitzpatrick`（例: "🧒🏽"）

どこまで正確に絵文字をカウントするかは、用途や要件によりますが、これらの絵文字を視覚的な文字数と一致させたい場合、`unicode_segmentation` などの外部クレートを利用する必要があります。

# 参考文献
- [Unicode® Technical Standard #51 UNICODE EMOJI](https://www.unicode.org/reports/tr51/)
- [Unicode® Emoji Chart Format](https://www.unicode.org/emoji/format.html#col-totals)