[Rust] 文字列 String から文字列スライス str へ変換される仕組み

Rust で文字列 `String` を文字列スライス `str` に変換するには、借用する (参照を得る) か、または `as_str` メソッドを使用します。

```rust
let s: String = String::from("str");

let ss: &str = &s;
// or
let ss: &str = s.as_str();
```

※説明のために変数全てで型指定をしていますが、`String::from` メソッドや `as_str` メソッドは戻り値の型指定を省略できます。

他にも変換手段がありますが、それらを含めて文字列 `String` から文字列スライス `str` に変換される仕組みについてまとめます。

文字列スライス `str` から文字列 `String` への変換については別記事にしました。

参考「[[Rust] 文字列スライス str から文字列 String への変換とその仕組み - Qiita](https://qiita.com/kerupani129/items/89f31bab79ebba43aeb4)」

# 1. 文字列 `String` を文字列スライス `str` に変換する 8 つの方法

本記事では以下のコードのような 8 つの変換方法について説明します。

※本質的には 3 つの変換 (※後述) の組み合わせになります。

```rust
let s: String = String::from("str");

let ss: &str = s.as_str();
// or
let ss: &str = &s;
// or
use std::ops::Deref;
let ss: &str = s.deref();
// or
let ss: &str = s.as_ref();
// or
let ss: &str = &s[..];
// or
use std::ops::Index;
let ss: &str = s.index(..);
// or
use std::borrow::Borrow;
let ss: &str = s.borrow();
// or
use std::str;
let ss: &str = str::from_utf8(s.as_bytes()).unwrap();
```

## 1.1. UTF-8 バイト列から文字列スライス `str` に変換

`String` 型から変換する前に、まずは UTF-8 バイト列からの変換を確認します。

```rust
use std::str;

let s: String = String::from("str");

let bytes: &[u8] = s.as_bytes();

let ss: &str = str::from_utf8(bytes).unwrap(); // &[u8] -> &str
```

参考「[from_utf8 in std::str - Rust](https://doc.rust-lang.org/std/str/fn.from_utf8.html)」

## 1.2. Deref 型強制による変換

`String` 型は `Deref<Target = str>` を実装しており、参照外し時における「Deref 型強制」によって `&String` 型から `&str` 型に型強制されます。

```rust
let s: String = String::from("str");

let ss: &str = &s;
// or
use std::ops::Deref;
let ss: &str = s.deref();
```

`String` 型における `Deref` トレイトの実装は以下のようになっています。

```rust
pub struct String {
    vec: Vec<u8>,
}
```

```rust
impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}
```

参考「[Deref - String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref)」

`from_utf8_unchecked` 関数は `from_utf8` 関数の安全でない版で、正しい UTF-8 バイト列かどうかを確認せずに文字列スライス `str` に変換します。

`from_utf8_unchecked` 関数の引数において `&Vec<u8>` から `&[u8]` に型強制されます。

参考「[from_utf8_unchecked in std::str - Rust](https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html)」

`as_str` メソッドや `as_ref` メソッドは、中で Deref 型強制を行っているだけです。

```rust:as_str&nbsp;メソッドの定義
impl String {
    // ...
    pub fn as_str(&self) -> &str {
        self
    }
    // ...
}
```

```rust:as_ref&nbsp;メソッドの定義
impl AsRef<str> for String {
    #[inline]
    fn as_ref(&self) -> &str {
        self
    }
}
```

参考「[as_str - String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html#method.as_str)」
参考「[AsRef&lt;str&gt; - String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html#impl-AsRef%3Cstr%3E)」

## 1.3. インデックス操作による変換

`String` 型ではインデックス操作を可能にするために `Index` トレイトを実装しています。

また、インデックス値に範囲境界を使用できるようになっていて、範囲 `..` すなわち `RangeFull` に関して以下のように実装しています。

```rust
impl ops::Index<ops::RangeFull> for String {
    type Output = str;

    #[inline]
    fn index(&self, _index: ops::RangeFull) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}
```

処理内容は `String` 型における `Deref` トレイトの実装と同じです。

参考「[Index&lt;RangeFull&gt; - String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html#impl-Index%3CRangeFull%3E)」
参考「[RangeBounds in std::ops - Rust](https://doc.rust-lang.org/std/ops/trait.RangeBounds.html)」(「範囲境界」)
参考「[Index in std::ops - Rust](https://doc.rust-lang.org/std/ops/trait.Index.html)」

よって、全範囲インデックスによって以下のように文字列 `String` から文字列スライス `str` に変換できます。

```rust
let s: String = String::from("str");

let ss: &str = &s[..];
// or
use std::ops::Index;
let ss: &str = s.index(..);
```

`borrow` メソッドは、中でインデックス操作を行っているだけです。

```rust:borrow&nbsp;メソッドの定義
impl Borrow<str> for String {
    #[inline]
    fn borrow(&self) -> &str {
        &self[..]
    }
}
```

参考「[Borrow&lt;str&gt; - String in std::string - Rust](https://doc.rust-lang.org/std/string/struct.String.html#impl-Borrow%3Cstr%3E)」

ちなみに Rust で文字列をそのままをインデックスで操作すると、Unicode の文字単位ではなく UTF-8 のバイト単位の操作になるため、注意が必要です。

# 2. 実際にはどの方法を使うか？

実質行っている処理はどれも同じですが、可読性を考えると `&s` で型指定するか `s.as_str()` が良いと思います。

※ `&s` に関しては戻り値の型指定必須。

`&s` と異なり、全範囲インデックスの指定によって明示的に `&str` 型に変換する `&s[..]` を使用することも考えられます。
変数束縛せずに値を比較したりする場合には `&s[..]` が便利かもしれません。