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 を実装しており、参照外し時における「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] に型強制されます。
参考「from_utf8_unchecked in std::str – Rust」
as_str メソッドや as_ref メソッドは、中で Deref 型強制を行っているだけです。
impl String {
// ...
pub fn as_str(&self) -> &str {
self
}
// ...
}
impl AsRef<str> for String {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
参考「as_str – String in std::string – Rust」
参考「AsRef – 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 – 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 メソッドは、中でインデックス操作を行っているだけです。
impl Borrow<str> for String {
#[inline]
fn borrow(&self) -> &str {
&self[..]
}
}
参考「Borrow – String in std::string – Rust」
ちなみに Rust で文字列をそのままをインデックスで操作すると、Unicode の文字単位ではなく UTF-8 のバイト単位の操作になるため、注意が必要です。
2. 実際にはどの方法を使うか?
実質行っている処理はどれも同じですが、可読性を考えると &s で型指定するか s.as_str() が良いと思います。
※ &s に関しては戻り値の型指定必須。
&s と異なり、全範囲インデックスの指定によって明示的に &str 型に変換する &s[..] を使用することも考えられます。
変数束縛せずに値を比較したりする場合には &s[..] が便利かもしれません。