Rustで文字列の先頭文字や部分文字列を取得する

## Rustで文字列の先頭文字を取得する

Rustの文字列には様々な型があるので初心者には操作がなかなか難しいです。
「文字列の先頭文字を取得する」という操作も、大きく分けて2通りあることに気づきました。

```rust
let text = "Hello World!";
```
この`text`という文字列の先頭文字`H`を取得することを考えましょう。
Rustでは文字列リテラルを変数に代入した時、`&'static str`型という`&str`型の一種を持ちます。なので、`text`は`&str`型です。

`&str`型には`.chars()`という、文字列を文字（`char`型）のイテレータ（`Chars`型）に変換するメソッドがあります。イテレータにすれば`.next()`で順に要素を前から一つずつ見ていくことができます。なので、

```rust
let ch1 = text.chars().next().unwrap();
println!("{}", ch1); // H
```

このように先頭文字を取得できます。イテレータは次の要素がないかもしれないので`Option`型を返すことから、`unwrap()`する必要があります。

**追記（2019/06/04）**

@pseudo_foxkeh さんに教えていただいた情報を載せておきます。

イテレータは`.next()`で順に要素を前から一つずつ見ていくことができますが、`.nth()`で直接 n 番目の要素を見ることもできます。というか、このメソッドの方が便利ですね。なので、`.nth()`を使うと

```rust
let ch1 = text.chars().nth(0).unwrap();
println!("{}", ch1); // H
```

このように書けます。なお、ここで得られる`ch1`は`char`型です。

<br>

もう一つの方法としてはスライスを使うことができます。感覚としてはPythonなどに近くて、

```rust
let ch2 = &text[..1];
println!("{}", ch2); // H
```

といけます。ここで得られる`ch2`は`&str`型です。

<br>

このように見るとスライスの方が便利のように見えますが、そうとも限りません。世の中の文字はASCII文字のような1バイトに収まるものばかりではないからです。

```rust
let text = "令和";
```

今度は漢字でやってみましょう。一つ目の方法は

```rust
let ch1 = text.chars().next().unwrap();
println!("{}", ch1); // 令
```

```rust
let ch1 = text.chars().nth(0).unwrap();
println!("{}", ch1); // 令
```

やはり上手くいきます。ところが二つ目の方法だと

```rust
let ch2 = &text[..1];
println!("{}", ch2); 
// thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside '令' (bytes 0..3) of `令和`', src/libcore/str/mod.rs:2027:5
```

とエラーを起こします。
エラーメッセージにある通り「令」という漢字は3バイトなのでこれではダメです。`let ch2 = &text[..3];`とすると上手くいきます。

**追記（2019/05/31）**

@termoshtt さんに教えていただいた情報を載せておきます。

今回「令」は3バイトなので、スライスをする際に3を指定してやる必要がありましたが、このようなバイト数の数値は毎回確認して決め打ちしておかねばならないのでしょうか？
実は、`.char_indices()`というメソッドを使えば、文字列の中の各文字の開始位置（スライスすべき位置）を得ることができます。
`.char_indices()`は`CharIndices`という型のイテレータを返します。この型は、`Chars`の拡張で、文字だけでなく文字の位置も含めてタプルとして保持します。
今回の例の場合、二文字目の開始位置を得てそれをスライスの終端とすればよいので、

```rust
let (i, _) = text.char_indices().nth(1).unwrap();
let ch2 = &text[..i];
println!("{}", ch2); // 令
```

または

```rust
let i = text.char_indices().nth(1).unwrap().0;
let ch2 = &text[..i];
println!("{}", ch2); // 令
```

他の例でも確認しておきましょう。

```rust
let text = "🍣🍺";
let (i, _) = text.char_indices().nth(1).unwrap();
let ch2 = &text[..i];
println!("{}", ch2); // 🍣
```
```rust
let text = "🍣🍺";
let i = text.char_indices().nth(1).unwrap().0;
let ch2 = &text[..i];
println!("{}", ch2); // 🍣
```

寿司の絵文字🍣は4バイトですが、やはり上手くいきます。

<br>

ここで問題になっているのは文字列のバイト数の取り方です。
`&str`という型自体は内部的には`&[u8]`、すなわち1バイト（=8ビット）の数が延々と続いているだけでそれを文字のかたまりごとに上手くスライスできる保証はありません。
一方、`&str`に備わっている`.chars()`を使えば、文字列に含まれるそれぞれの文字が何バイトであろうと、うまくバイトを区切って一文字ずつ取得し、イテレータにすることができます。また、`.char_indices()`を使えば文字列に含まれる各文字の開始位置がわかるので、それを使ってスライスをすることができます。

というわけで、アルファベットや単純な記号のみを使うのであればスライスで簡潔に済みますが、より複雑な文字を扱いたい場合、スライスだと逆に記述量が多くなってしまい、`.chars()`を使う方がよい、というのが実情のようです。さらなる別の方法があればぜひ教えてくださいm(_ _)m

## Rustで文字列の部分文字列を取得する

*@pseudo_foxkeh さんの情報をもとに追記しました。ありがとうございます。(2019/06/04)*

<br>

先頭文字だけでなく、今度は部分文字列を取得することを考えてみましょう。
単純なASCII文字だけから成る文字列であれば、上記の通り直接スライスで範囲を指定して簡単にできるので、そのような簡単なケースは飛ばして最初から複雑な文字列を考えてみましょう。

```rust
let text = "🍣🍺大好き💖💖";
```

この文字列の真ん中の`大好き`の部分を取り出していきたいと思います。 

Rustのイテレータには`.enumerate()`というインデックス付きのイテレータを返すメソッドがあります（他の言語にもこのような「インデックス付きのものを返す」機能はよくありますね）。
なので、このメソッドを用いて得られるインデックスを利用して`filter`して、残った文字を畳み込みでくっつけてあげることを考えると、

```rust
let str1 = text.chars().enumerate().filter(|&(i,_)| i >= 2 && i < 5).fold("".to_string(), |s, (_, c)| format!("{}{}", s, c));
println!("{}", str1); // 大好き
```

こんな感じで頑張ってワンライナーで書くことができます。
もちろんワンライナーにこだわらなければ

```rust
let mut str1 = "".to_string();
for (i, c) in text.chars().enumerate() {
  if i >= 2 && i < 5 {
    str1.push(c);
  }
}
println!("{}", str1); // 大好き
```

でもいいですね。
なお、ここで得られる`str1`は`String`型です。

<br>

一方で、あくまでスライスにこだわる場合、

```rust
let begin = text.char_indices().nth(2).unwrap().0;
let end = text.char_indices().nth(5).unwrap().0;
let str2 = &text[begin..end];
println!("{}", str2); // 大好き
```

と書けます。なお、ここで得られる`str2`は`&str`型です。

先ほどの先頭文字の型（`&str`か`char`か）の場合と異なり、`String`と`&str`は簡単に行き来できるので、返り値の型に神経質になる必要はそんなになさそうです。

<br>

こちらも、さらなる別の方法があればぜひ教えてくださいm(_ _)m

ちなみに、「Rust 部分文字列」でググると、古い記事の中には`.slice_chars()`メソッドを紹介しているものがありますが、すでに Deprecated になっているようです（参照: https://github.com/rust-lang/rust/pull/26914/files#diff-2544e5e5ce71797344d5ec6acde08b87R555 ）。

理由としては, *can be implemented with char_indices and hasn't seen enough use to justify inclusion* と書かれているので、やはり`.char_indices()`を使ってね、ということみたいです。