4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-09-06

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

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

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

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

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

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 バイト列からの変換を確認します。

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

1.2. Deref 型強制による変換

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

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

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

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

pub struct String {
    vec: Vec<u8>,
}
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

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

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

参考「from_utf8_unchecked in std::str - Rust

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

as_str メソッドの定義
impl String {
    // ...
    pub fn as_str(&self) -> &str {
        self
    }
    // ...
}
as_ref メソッドの定義
impl AsRef<str> for String {
    #[inline]
    fn as_ref(&self) -> &str {
        self
    }
}

参考「as_str - String in std::string - Rust
参考「AsRef<str> - String in std::string - Rust

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

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

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

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<RangeFull> - String in std::string - Rust
参考「RangeBounds in std::ops - Rust」(「範囲境界」)
参考「Index in std::ops - Rust

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

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

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

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

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

参考「Borrow<str> - String in std::string - Rust

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

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

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

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

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

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?